据我所知,当代码包含未初始化变量的使用时,Valgrind应报告错误。在下面的玩具示例中,printer
未初始化,但程序“愉快地”打印消息。
#include <iostream>
class Printer {
public:
void print() {
std::cout<<"I PRINT"<<std::endl;
}
};
int main() {
Printer* printer;
printer->print();
};
当我用Valgrind测试这个程序时,它不报告任何错误。
这是预期的行为吗?如果是的话,为什么呢?
答案 0 :(得分:4)
this
,因此根本不使用该变量。以上与开启或关闭优化无关。
事实上,在优化代码中,变量根本不存在 - 甚至不作为内存分配。
关于类似案件的问题:Extern variable only in header unexpectedly working, why? 。
1 默认情况下,类体中定义的所有方法都是内联的。
是的。调用该方法需要this
指向一个实际的,初始化的对象的格式良好。正如Nir Friedman指出的那样,编译器可以自由地假设并在该基础上进行优化(而IIRC甚至可以使用-O0
进行这种优化!)。
我个人希望有问题的特定代码可以在任何实际条件下工作(因为指针值确实无关紧要),但我绝不会依赖它。 您应该立即修复您的代码。
要检测Clang / GCC中未初始化变量的使用情况,请使用选项-Wuninitialized
(或简单地使用-Wall
,其中包含此标志)。
-Wuninitialized
应该主要涵盖堆栈分配内存的使用,尽管我猜一些堆栈分配的数组的使用可能仍会失误。有些编译器可能会支持使用-fsanitize=...
选项对未初始化的读取进行额外的运行时检查,例如-fsanitize=memory
in Clang(thx,chtz)。这些检查应该包括边缘情况以及堆分配内存的使用。
答案 1 :(得分:0)
main()
函数具有未定义的行为,因为printer
未初始化且语句printer->print()
都访问printer
的值并通过->
取消引用它成员函数的调用。
但实际上,允许编译器通过简单地假设它不存在来处理未定义的行为。然后编译器可以选择遵循一系列逻辑;
printer->print()
之类的语句时,这意味着允许推断printer
具有可以在不引入未定义行为的情况下访问和解除引用的值。printer
必须已初始化(通过某些方式对编译器不可见)指向有效对象。printer->print()
将导致Printer::print()
的调用。Printer::print()
的定义,因此它可以简单地内联它,并执行语句std::cout<<"I PRINT"<<std::endl
。printer
来生成该输出,因此可以优化对printer
中名为main()
的变量的任何引用。如果编译器遵循上述逻辑顺序,程序将只打印I PRINT
并退出,而不会以可能触发Valgrind报告的方式访问任何内存。
如果您认为上述内容听起来很牵强,那么您就错了。 LLVM / Clang是一个编译器,它在概念上遵循与我所描述的非常相似的逻辑链。有关更多信息,请查看LLVM项目博客link to first article,second article和 third article