如何在GCC/Clang中检测此类未定义行为?
你这段代码的问题很明确:类C的成员变量I没被初始化就被getI()读取了——因为你没写默认构造函数,编译器生成的默认构造不会给非静态成员变量赋初始值,这妥妥是未定义行为。但常规的-fsanitize=undefined没检测到,是因为这个场景不在UBSAN默认的检测清单里,得用专门的选项来抓它。
下面分别说GCC和Clang的解决办法:
用GCC检测
编译时静态检测
GCC的-Wuninitialized警告需要配合优化选项(比如-O1及以上)才能发挥作用——毕竟编译器得在优化过程里跟踪变量的初始化状态。你可以这么编译:
g++ -Wall -pedantic -O1 -Wuninitialized -o undef undef.cxx
编译时直接会弹出警告,告诉你c.C::I没初始化就被用了:
undef.cxx: In function ‘int main()’: undef.cxx:10:28: warning: ‘c.C::I’ is used uninitialized in this function [-Wuninitialized] 10 | std::cout << c.getI() << std::endl; | ~~~~~~^~
运行时动态检测
如果想在运行时抓这个问题,可以用GCC的内存 sanitizer(-fsanitize=memory),不过要注意这个工具需要特殊的编译环境(比如链接阶段也要用MSAN,而且系统库最好是经过MSAN插桩的版本):
g++ -Wall -pedantic -fsanitize=memory -o undef undef.cxx && ./undef
运行时会直接报错,明确指出你读取了未初始化的内存。
用Clang检测
编译时静态检测
Clang的-Wuninitialized同样要搭配优化选项才能生效,试试这条命令:
clang++ -Weverything -O1 -Wuninitialized -o undef undef.cxx
编译时会得到清晰的警告,甚至还会提示你怎么修复:
undef.cxx:10:28: warning: variable 'c' is uninitialized when used here [-Wuninitialized] std::cout << c.getI() << std::endl; ^~~~~~ undef.cxx:9:7: note: initialize the variable 'c' to silence this warning C c; ^ = C()
运行时动态检测
Clang的-fsanitize=memory也能在运行时检测到这个问题,用法和GCC差不多:
clang++ -Weverything -fsanitize=memory -o undef undef.cxx && ./undef
为啥默认UBSAN没反应?
简单说,UndefinedBehaviorSanitizer(UBSAN)默认不检测未初始化自动变量/类成员的读取——这类检测属于MemorySanitizer(MSAN)的负责范围,或者需要编译器在优化阶段做静态分析(也就是刚才说的-Wuninitialized加优化)。UBSAN默认盯的是整数溢出、空指针解引用这类其他类型的未定义行为。
内容的提问来源于stack exchange,提问作者malat

