An unknown blog

KaTeX 炸了不想修。

0%

因为我们学校里的在线评测系统没有办法屏蔽 #pragma GCC optimize 预处理指令,因此出现了如下的状况:

因此,今天我决定从根源上禁止这些行为。

正好前两天看到洛谷上屏蔽手动 O2 O3 Ofast 的方式,于是今天自己就来试着修改 GCC 的源代码,并计划手动编译并上线测试。

需求整理

  1. 魔改编译器禁止手动优化;
  2. 编译器版本之间可以分离,不予系统自带的编译器冲突;
  3. GCC 9 + GCC 12

准备工作

如果你也是某个 OJ 的运维/管理,直接在服务器上构建源代码会更好,但是注意资源占用。

如果你只是自用,那么无所谓。

但是前提是一定要在一个 Linux 环境下操作。(我也只写了 Linux (Ubuntu) 的记录啥的)

正式安装

安装需要的库

文档是按照最新的正式版 GCC 12 写的,向下兼容。

根据 Prerequisites for GCC 所说,总结下来,最省心的情况(我的情况)需要如下条件:

  • 通常是系统自带的
    • Ubuntu >= 20.04 (当然可以不是这个发行版,但 CentOS 算了)
    • 一个 POSIX Shell,但不包括 zsh
    • GCC >= 9.4
    • Python 3
    • ssh, git 等
  • 需要手动构建安装的

安装依赖库的过程相对简单。步骤总价下来无外乎以下几点:

  1. 下载依赖库源码包
  2. 上传源码包到服务器
  3. 解压源码包
  4. cd 到源码包解压到的目录
  5. 执行:./configure && make && sudo make install

一定要从上往下依次安装。

配置

现在假定你的源代码存放在 src 目录下。在 src 的上级菜单新建文件夹 obj

1
2
3
4
5
/path/to/your/project
|--- src
|--- configure
`--- [Another Files]
`--- obj

obj 目录下执行 configure 指令。

比方说,我的指令是:

1
../src/configure --with-pkgversion="gcc-12.2.0_Imken_Customized (Build On JudgeMachine)" --host=x86_64-pc-linux-gnu --disable-multilib

(32 位机器大概不适用)

--with-pkgversiong++ --version 时显示的版本号(下面括号里的内容),具体如下:

1
2
3
$ g++ --version
g++ (gcc-12.2.0_Imken_Customized (Build On JudgeMachine)) 12.2.0
Copyright (C) ...

--host=x86_64-pc-linux-gnu 是说目标平台的架构。

更多配置项目参见 Installing GCC: Configuration

构建

本过程大约需要 $0.6~4$ 小时。期间服务器的资源会爆满。建议使用 screen 在后台运行。

构建很简单,在 obj 文件夹下执行 make 即可。或者说,机子够强,可以并行构建,执行 make -j <线程数> 即可。

然后就是艰难的等待。我个人建议在机子终端面前守着,防止意外错误。

我遇到了一个错误,libgsl.so.23 没找到。这个 issue 解决得还不错,就是修改 ld 寻找 so 文件的目录。

1
2
LD_LIBRARY_PATH=/usr/local/lib/
export LD_LIBRARY_PATH

若构建中断,可以从断点重新开始构建。make 还是比较智能的。

安装

要是不出意外的话,构建将会在很长一段时间之后结束。此时可以执行 make install

但!是!我要版本隔离!

make DESTDIR=<目标目录> install 可以让 GCC 安装在指定目录。比如:

1
2
make DESTDIR=/opt/imken_oi/gcc/1220 install  # GCC 12.2.0
make DESTDIR=/opt/imken_oi/gcc/940 install # GCC 9.4.0

目录不存在的话会自动新建。

安装好的 GCCbin<DESTDIR>/usr/local/bin/

总结

等死我了。最后 make install 的可执行文件对于每一台机子都不一样,不可直接一直。烦死了。白编译三个小时。

版权信息 / Copyrights

头图:Pixiv PID 106462140 使用未经授权 侵权联系我删除

Header image of this article: Pixiv PID 106462140; Unauthorized use, infringement contact me to delete.

Link: https://www.pixiv.net/artworks/106462140

若非明确指明,本文所探讨的标准为 C++14。

引入

阅读下面的一段代码,判断输出。

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
int i = 1;
int arr[10];
int main()
{
while (i < 10)
arr[++i] = arr[i - 1] + 1;
for (i = 1; i < 10; i++) {
std::cout << arr[i] << ' ';
}
}

A. 0 1 2 3 4 5 6 7 8
B. 0 1 1 2 2 3 3 4 4

在 g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 上,答案是 B。
在 Dev-Cpp 5.11 (TDM-GCC 4.9.2) 上,答案是 A。

为什么会出现这种情况呢?
注意到 Ubuntu 环境下的编译输出:

1
std.cpp:7:7: warning: operation on ‘i’ may be undefined [-Wsequence-point]

在变量 i 上的操作可能是未定义的。

我们的一位同学就是因为这个“undefined behavior (UB)”导致一份代码浪费了一个下午的时间来调试。

两边编译器的实现是不一样的:
Ubuntu:

1
2
3
LET TMP_VAR <- arr[i - 1] + 1
i <- i + 1
arr[i] <- TMP_VAR

Windows:

1
2
3
i <- i + 1
LET TMP_VAR <- arr[i - 1] + 1
arr[i] <- TMP_VAR

建议在 OI 赛场上,所有代码都放在考场提供的 NOI Linux 上进行编译,并务必添加如下参数:

1
-std=c++14 -O2 -Wall

常见未定义行为

数组越界访问

如下代码:

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
char s[11];

int main()
{
cin >> s + 1;
cout << s + 1;
}

在部分编译器上,即使输入的字符串长度大于 $10$,但是输出的字符串就是原串;但是在另一部分编译器上,输出字符串只能输出⑨个字符。更可怕的是,编译器没有对此报错。

在 C++20 标准中,部分编译器已经开始对此报错。新标准抛弃了 cinchar* 的支持,但是保留了对 char[] 的支持。(cout 并没有放弃支持)

1
2
3
4
5
6
7
src.cpp: 在函数‘int main()’中:
src.cpp:7:9: 错误:no match for ‘operator>>’ (operand types are ‘std::istream’ {aka ‘std::basic_istream<char>’} and ‘char*’)
7 | cin >> s + 1;
| ~~~ ^~ ~~~~~
| | |
| | char*
| std::istream {aka std::basic_istream<char>}

我记得之前洛谷上有个讨论就是关于这个的 但是我找不到了

不确定的运算顺序

文章开头的代码就属于这种类型,只不过藏得深了一些。

求值任何表达式的任何部分,包括求值函数参数的顺序都未指明(除了下列的一些例外)。编译器能以任何顺序求值任何操作数和其他子表达式,并且可以在再次求值同一表达式时选择另一顺序。

C++ 中无从左到右或从右到左求值的概念。这不会与运算符的从左到右及从右到左结合性混淆:表达式 a() + b() + c() 由于 operator+ 的从左到右结合性被分析成 (a() + b()) + c(),但在运行时可以首先、最后或者在 a()b() 之间对 c() 求值。

  1. 如果某个内存位置上的一项副作用相对于同一个内存位置上的另一副作用是无顺序的,那么它的行为未定义。
    1
    2
    3
    4
    5
    i = ++i + 2;       // 具有良好定义
    i = i++ + 2; // C++17 前行为未定义
    f(i = -2, i = -2); // C++17 前行为未定义
    f(++i, ++i); // C++17 前行为未定义,C++17 起未指明
    i = ++i + i++; // 行为未定义
  2. 如果某个内存位置上的副作用相对于使用在同一个内存位置中的任何对象的值的值计算是无顺序的,那么它的行为未定义。
    1
    2
    3
    cout << i << i++; // C++17 前行为未定义
    a[i] = i++; // C++17 前行为未定义
    n = ++i + i; // 行为未定义

参见 求值顺序 - cppreference.com

有符号整数自然溢出

记得之前看过一个笑话。

判断真假:$\exists x\in \Z,x > x + 1$

  • 数竞生:假命题
  • 信竞生:当 $x =2147483647$ 时,$x+1==-2147483648$,是真命题。

但是,这个信竞生不知道的是,编译器不这么认为。

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
if (a > a + 114514) cout << "Meow?";
else cout << "Meow!";
}

输入:2147483647
输出:Meow!

编译器认为 $a$ 和 $a+114514$ 都应该在 int 范围内,因此 a > a + 114514 恒为假,所以把 "Meow?"一行给优化掉了。

但是无符号整数自然溢出不是未定义行为。如果 a 声明为无符号整数,程序执行符合预期。
(这也就是写字符串哈希的时候使用 ull 自然溢出的原因)

未初始化的标量

1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main()
{
bool a;
cout << a;
}
1
2
3
4
ub.cpp: In function ‘int main()’:
ub.cpp:6:13: warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
6 | cout << a;
| ^

输出 0/1 都是可能(且合法)的。

总结

一个符合标准的实现可以在假定未定义行为永远不发生(除了显式使用不严格遵守标准的扩展)的基础上进行优化,可能导致原本存在未定义行为(例如有符号数溢出)的程序经过优化后显示出更加明显的错误(例如死循环)。因此,这种未定义行为一般应被视为bug。

“未定义行为 - 维基百科,自由的百科全书,” 维基百科, Apr. 1, 2023. (访问时间 Apr. 1, 2023).

只能尽量避免这种阴间情况的发生了吧。给建议的话,减少自以为是的压行

版权信息 / Copyrights

头图:Pixiv PID 105783009 使用未经授权 侵权联系我删除

Header image of this article: Pixiv PID 105783009; Unauthorized use, infringement contact me to delete.

Link: https://www.pixiv.net/artworks/105783009

SPOJ 原题传送门 洛谷RMJ传送门

这是一道数论题。

思路

因为要求找到的整数 $x$ 是一个互质于 $a_i$ 的数,所以很显然 $x$ 为质数的时候最优。

根据互质的一些性质,$a_i$ 和 $x$ 没有共同的质因数,所以可以标记所有 $a_i (1\le i\le n)$ 的质因数,然后选取最小的未被标记的质数。

举个例子:
$\rm&#123a = \tt&#123[11, 45, 14, 19, 81]}}$
标记如下

故选择13作为 $x$。


看到这里,就滚去写代码罢!

Read more »

今天收到了一封邮件。

Title: Report Domain: immccn123.xyz Submitter: protection.outlook.com From: dmarcreport@microsoft.com To: [Private]@outlook.com

Body:
This is a DMARC aggregate report from Microsoft Corporation. For Emails received between 2022-12-19 00:00:00 UTC to 2022-12-20 00:00:00 UTC.

You’re receiving this email because you have included your email address in the ‘rua’ tag of your DMARC record in DNS for immccn123.xyz. Please remove your email address from the ‘rua’ tag if you don’t want to receive this email.

Attachment:
protection.outlook.com!immccn123.xyz!1671408000!1671494400.xml.gz

把我吓到了。
那个附件给各位看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<feedback xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<version>1.0</version>
<report_metadata>
<org_name>Outlook.com</org_name>
<email>dmarcreport@microsoft.com</email>
<report_id>1bfd21e924d04d1096bf8786f6b9c376</report_id>
<date_range>
<begin>1671408000</begin>
<end>1671494400</end>
</date_range>
</report_metadata>
<policy_published>
<domain>immccn123.xyz</domain>
<adkim>r</adkim>
<aspf>r</aspf>
<p>quarantine</p>
<sp>quarantine</sp>
<pct>100</pct>
<fo>0</fo>
</policy_published>
<record>
<row>
<source_ip>136.143.188.56</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<!-- Here!!! (This line is not in the file) -->
<dkim>fail</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<envelope_to>outlook.com</envelope_to>
<envelope_from>immccn123.xyz</envelope_from>
<header_from>immccn123.xyz</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>immccn123.xyz</domain>
<selector>imkn</selector>
<result>temperror</result>
</dkim>
<spf>
<domain>immccn123.xyz</domain>
<scope>mfrom</scope>
<result>pass</result>
</spf>
</auth_results>
</record>
<record>
<row>
<source_ip>136.143.188.56</source_ip>
<count>1</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>pass</dkim>
<spf>pass</spf>
</policy_evaluated>
</row>
<identifiers>
<envelope_to>outlook.com</envelope_to>
<envelope_from>immccn123.xyz</envelope_from>
<header_from>immccn123.xyz</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>immccn123.xyz</domain>
<selector>imkn</selector>
<result>pass</result>
</dkim>
<spf>
<domain>immccn123.xyz</domain>
<scope>mfrom</scope>
<result>pass</result>
</spf>
</auth_results>
</record>
</feedback>
</code></pre>

查了一下,根据xml文件中的信息,是dkim检验失败。

我寻思着, $\sf&#123Zoho\space Mail}$也是个大厂啊,怎么偏偏就出一些小问题呢?(像什么校验失败的问题)

检查后发现,auth_result/dkim/result的地方是temperror。那就说明不是Outlook抽风就是CloudFlare DNS抽风了(

顺便再介绍下DMARC记录吧。

什么是DMARC

“DMARC”是Domain-based Message Authentication, Reporting and Conformance的英文首字母缩写,翻译过来就是

基于域的消息认证,报告和一致性

就是个拦截伪造电子邮件的协议,以SPF和DKIM为基础。

为什么要防着伪造电子邮件呢?
答案是,SMTP协议本身没有机制鉴别寄件人的真正身份,电子邮件的“From”一栏可以填上任何名字,于是伪冒他人身份来网络钓鱼或寄出垃圾邮件便相当容易,而真正来源却不易追查。

于是,几家邮件大厂一拍即合,创造了DMARC协议。

使用DMARC很简单,就是需要先配置好SPF,DKIM,然后添加一个TXT记录指向_dmarc:

v=DMARC1; p=quarantine; pct=100; rua=mailto:###@outlook.com

各项常用值含义如下:

标签名 用处 例子
v 协议版本(其实截至目前也只有v1) v=DMARC1
pct 要筛查的邮件百分比(哪儿那么多屁话 全部筛啊) pct=100
ruf 出锅了的邮件的数据报告地址(出锅就报) ruf=mailto:###@outlook.com
rua 出锅了的邮件的汇总数据报告地址 rua=mailto:###@outlook.com
p 怎么处理出锅的邮件:none仅报告 quarantine垃圾邮件之类的,或者不给过 reject不给过 p=quarantine
sp 怎么处理子域发送的邮件 sp=reject

常用的就这些。

上面的这封邮件就是Outlook在检测到DMARC异常的时候的通知。同时,p=quarantine在Outlook中似乎是阻止。

另:请不要怀疑报告中的IP,那个IP是Zoho的。

参考链接

欢迎使用WordPress。这是您的第一篇文章。编辑或删除它,然后开始写作吧!

本站点于2021年12月创建,于2022年下半年更换至 WordPress 引擎。

服务器基本信息:

厂商Hostinger
配置不晓得
价格1400/4yr
IP不知道
OS不知道
DNS/CDNCloudFlare/Gcore
Blog SoftwareWordPress Latest
PHP Version***