sha*_*oth 4 c++ compilation clang compiler-optimization undefined-behavior
这是此推文代码的变体,只是更短,不会对菜鸟造成任何损害。我们有这样的代码:
typedef int (*Function)();
static Function DoSmth;
static int Return7()
{
return 7;
}
void NeverCalled()
{
DoSmth = Return7;
}
int main()
{
return DoSmth();
}
Run Code Online (Sandbox Code Playgroud)
您会发现NeverCalled()代码中从未调用过它,不是吗?以下是选择 clang 3.8 时编译器资源管理器显示的内容
-Os -std=c++11 -Wall
Run Code Online (Sandbox Code Playgroud)
发出的代码是:
NeverCalled():
retq
main:
movl $7, %eax
retq
Run Code Online (Sandbox Code Playgroud)
就好像NeverCalled()之前实际调用过DoSmth()并将DoSmth函数指针设置为Return7()函数一样。
如果从内部删除函数指针分配,NeverCalled()如下所示:
void NeverCalled() {}
Run Code Online (Sandbox Code Playgroud)
那么发出的代码是这样的:
NeverCalled():
retq
main:
ud2
Run Code Online (Sandbox Code Playgroud)
后者是值得期待的。编译器知道函数指针肯定为空,并且使用空函数指针调用函数是未定义的行为。
前面的代码并不是真正所期望的。不知何故,编译器决定Return7()调用,尽管它没有在任何地方直接调用,并且函数指针赋值位于未调用的函数内部。
是的,我知道 C++ 标准允许面向具有未定义行为的代码的编译器执行此操作。它是如何做到这一点的呢?
clang 是如何发出这个特定的机器代码的?
NeverCalled是用词不当。任何全局函数都可能被调用(例如,由不同翻译单元中的全局对象的构造函数)。
顺便说一句,这是将该 TU 合并到没有 UB 的程序中的唯一方法。在本例中,main返回 7。
设为NeverCalled静态,main并将编译为空代码。