谈 Sanitizer 的使用—未定义行为动态查错
本工具可以在 OI 系列竞赛中使用(只要你的省份提供了 NOI Linux)。
Sanitizer
在编译时通过加入开关 -fsanitize=undefined
来检查未定义行为。
只有 Clang 和 Linux 环境下的GCC可以使用这个功能。(所以立马换到linux吧)
先来看一个例子:
1 |
|
显然这段代码会发生有符号整数溢出,这是一个未定义行为。输入 g++ test2.cpp -o test2 -Wall -fsanitize=undefined
。进行运行,得到结果:
$ ./test2 test2.cpp:6:11: runtime error: signed integer overflow: 1000000000 * 1000000000 cannot be represented in type 'int' -1486618624
我们发现,-fsanitize
会将错误输出到 stderr
流中,并且会给出行号以及具体执行的值。在遇到超级多整数乘法的时候,你只知道最后是一个负数,说明你溢出了,但是你很难找到到底是哪一行溢出了。这样就可以快速找出溢出的行。
常见未定义行为
有符号整数溢出
这是一个最常见的错误。无论是你少开了一个 long long,还是忘记答案要用 long long 存,都会溢出,而有时候溢出的结果还是一个正数,这就使发觉这个错误变得十分困难。
好在只要一直开着-fsanitize=undefined
,就可以随时将溢出的情况汇报。
样例如上。
访问栈上未初始化的变量
谁没写过这样的代码呢?
1 |
|
这也是一个未定义行为,但很可惜,-fsanitize=undefined
检测不到这个错误。
但是在 -O2
条件下的 -Wall
可以检测到这个错误。
$ g++ test2.cpp -o test2 -Wall -fsanitize=undefined -O2 test2.cpp: In function ‘int main()’: test2.cpp:7:20: warning: ignoring return value of ‘int scanf(const char*, ...)’ declared with attribute ‘warn_unused_result’ [-Wunused-result] 7 | int x;scanf("%d",&x); | ~~~~~^~~~~~~~~ test2.cpp:5:9: warning: ‘ans’ may be used uninitialized in this function [-Wmaybe-uninitialized] 5 | int ans; | ^~~
数组越界
直白的错误。
1 |
|
-fsanitize=undefined
可以准确报出行号以及访问的下标。
$ ./test2 test2.cpp:8:12: runtime error: index 5 out of bounds for type 'int [5]' test2.cpp:8:13: runtime error: store to address 0x559f3230a0d4 with insufficient space for an object of type 'int' 0x559f3230a0d4: note: pointer points here 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ^
对性能的影响
1 |
|
这里有一份爆搜代码。我们尝试考察它对 10 10 0
这个输入的用时。
测试环境 CPU: 12th Gen Intel i5-12500H (16) @ 3.110GHz
。
用时如下(未做多次实验取平均值):
用时 | |
---|---|
无开关 | 0.901s |
-O2 |
0.574s |
-fsanitize=undefined |
0.977s |
-fsanitize=undefined -O2 |
0.593s |
发现在递归场景下,似乎性能影响不大。
再来看一个大量数组操作的场景。
1 |
|
用时如下(未做多次实验取平均值):
用时 | |
---|---|
无开关 | 0.376s |
-O2 |
0.249s |
-fsanitize=undefined |
0.563s |
-fsanitize=undefined -O2 |
0.328s |
可以看到性能有略微损失,这是动态差错不可避免的。性能损失在可以接受的范围内。
综上,建议你在编写每一份代码时,都在编译命令添加上 -fsanitize=undefined
。