各大C++编译器可以编译输出UB提示信息吗?
如题,如果没有这个功能,为啥不直接编译后输出信息告诉我们哪些代码是UB,这样不就可以极大降低我们写出UB代码了么?
C++编译器warning一坨一坨的,已经告诉你了。但是还有一部分未定义行为其实是运行时行为,运行时行为很多不能静态分析出来。
clang10有,针对悬垂指针的,编译时就能查出,目前尚在完善中,我之前写了一篇介绍的文章:
日撸代码100行:C++编译器已可识别dangling pointer(悬垂指针)?zhuanlan.zhihu.com如果是越界、野指针等,需要将程序运行一下,很多工具,如sanitizer,gcc/clang在编译的时候加个选项,然后一运行就能告诉你哪一行代码越界了。santitizer教程:
https://github.com/google/sanitizers/wiki/AddressSanitizer?github.com你是不是编译程序都不加-Wall
、-pedantic
的啊……
不过确切来说UB和上面提到的报warning并不是包含关系,所以的确有很多UB行为是漏网之鱼。
评论指出我后面写的是未指定行为,不是未定义行为,所以不用往下看了。
比如求值顺序,cpprefence上就有例子:
求值顺序 - cppreference.com?zh.cppreference.com求值任何表达式的任何部分,包括求值函数参数的顺序都是未说明的(除了下列的一些例外)。编译器能以任何顺序求值任何操作数和其他子表达式,并且可以在再次求值同一表达式时选择另一顺序。
C++ 中无从左到右或从右到左求值的概念。这不会与运算符的从左到右及从右到左结合性混淆:表达式a() + b() + c()
由于 operator+ 的从左到右结合性被分析成(a() + b()) + c()
,但可在运行时首先或者最后或者a()
和b()
之间对c
函数调用求值:
#include &
int a() { return std::puts("a"); }
int b() { return std::puts("b"); }
int c() { return std::puts("c"); }
void z(int, int, int) {}
int main() {
z(a(), b(), c()); // 允许全部 6 种输出排列
return a() + b() + c(); // 允许全部 6 种输出排列
}
严格来说这里就有UB的存在,但这个UB有没有影响又是另一回事了——你真的需要依赖abc三个函数输出的顺序吗?
如果这种UB都要汇报,那report文件怕是比include展开的源代码还长了。而且这种UB除了用单参数函数式去写以外根本没法规避。
(如果有contract的话,或许可以通过更严格的expects/ensures来促使编译器发现因为UB而与开发者预期不一致的情况?但是到时候怕是1行C++配上100行expects,还不一定能覆盖得全)
对于悬垂指针、内存泄漏这种,我觉得不叫UB,这种叫bug……
如果想让编译器输出所有bug……这应该算是翻版停机问题了吧……
是的,有的。
gcc
https://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan/?developers.redhat.comclang
http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html?clang.llvm.orgClang 11 documentation?clang.llvm.org
理论上来说是可以的,实际上很多警告内容都是在反映UB,所以建议多关注一下警告
《C专家编程》里写到过这个问题
当时在C/C++早期,就有人提议把lint(检查程序)融合在编译器里,理由就是能够减少不安全的代码.但是被那些编写编译器的人否决了,认为功能应该最简.
现在的C/C++编译器已经初步具备了lint程序的部分功能(Warning部分),但是受以前的影响,lint程序的功能现在仍然没有完全并入编译器里.所以如果现在想要写出更加安全的代码,可以手动对代码使用cppcheck或pc-lint,并在编译参数里加上-Wall.
只用过clang和gcc,他们都是有warning的,而warning里有很多是可能引起ub的代码,比如数组越界,还有比如x++ + ++x这种坑爹玩意儿
但ub太多了不可能全部查到的吧……
推荐阅读: