TL;DR:在未优化的代码中,您的 CLANG++ 为返回值留出了 4 个字节main
并根据 C++(包括 C++11)标准将其设置为零。它生成了一个代码main
不需要它的功能。这是未优化的副作用。通常,未经优化的编译器会生成它可能需要的代码,然后最终不再需要它,并且没有采取任何措施来清理它。
因为你正在编译-O0
对代码进行的优化非常少(-O0
可能会删除死代码等)。试图理解未优化代码中的工件通常是一种浪费。未优化代码的结果是额外的加载和存储以及原始代码生成的其他工件。
在这种情况下main
很特殊,因为在 C99/C11 和 C++ 中,标准有效地表明,当到达外部块时main
默认返回值为0。C11标准 http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf says:
5.1.2.2.3 程序终止
1 如果 main 函数的返回类型是与 int 兼容的类型,则从
对 main 函数的初始调用相当于使用该值调用 exit 函数
由主函数作为其参数返回;11)到达终止的 }
main函数返回值0。如果返回类型与 int 不兼容,
返回到主机环境的终止状态未指定。
The C++11标准 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf says:
3.6.1 主要功能
5) main 中的 return 语句具有离开 main 函数的效果(使用自动销毁任何对象)
存储持续时间)并以返回值作为参数调用 std::exit。如果控制到达终点
main 没有遇到 return 语句,效果就是执行
return 0;
在 CLANG++ 版本中,您默认使用未优化的 64 位代码,返回值 0 位于dword ptr [rbp-4]
.
问题是您的测试代码有点太琐碎,无法看出这个默认返回值是如何发挥作用的。这是一个应该是更好演示的示例:
int main() {
int a = 3;
if (a > 3) return 5678;
else if (a == 3) return 42;
}
此代码有两个退出显式退出点return 5678
and return 42;
但没有最终结果return
在函数的末尾。如果}
达到时默认返回 0。如果我们检查神箭输出 https://gcc.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,lang:c%2B%2B,source:'int+main()+%7B%0A++++int+a+%3D+3%3B%0A++++if+(a+%3E+3)+return+5678%3B%0A++++else+if+(a+%3D%3D+3)+return+42%3B%0A%7D%0A%0Aint+foo()+%7B%0A++++return+1234%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:37.46397694524496,l:'4',m:100,n:'0',o:'',s:0,t:'0'),(g:!((g:!((g:!((h:compiler,i:(compiler:clang500,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',trim:'1'),lang:c%2B%2B,libs:!(),options:'-std%3Dgnu%2B%2B11+-Wall+-stdlib%3Dlibc%2B%2B+-O0+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+clang+5.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),header:(),k:50,l:'4',m:70.23208879919274,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:1,editor:1),l:'5',n:'0',o:'%231+with+x86-64+clang+5.0.0',t:'0')),header:(),l:'4',m:29.767911200807262,n:'0',o:'',s:0,t:'0')),k:48.24074074074074,l:'3',n:'0',o:'',t:'0'),(g:!((g:!((h:compiler,i:(compiler:g73,filters:(___0:(),b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',jquery:'3.2.1',length:1,prevObject:(___0:(sizzle1504678313932:(undefined:(legend:!(11,0,'1')))),length:1,prevObject:(___0:(jQuery3210325021482824634061:(display:''),sizzle1504678313932:(undefined:(legend:!(11,0,'1')))),length:1)),trim:'1'),lang:c%2B%2B,libs:!(),options:'-std%3Dgnu%2B%2B11+-Wall+-O0+-fverbose-asm',source:1),l:'5',n:'0',o:'x86-64+gcc+7.3+(Editor+%231,+Compiler+%232)+C%2B%2B',t:'0')),header:(),k:50,l:'4',m:74.06659939455096,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compiler:2,editor:1),l:'5',n:'0',o:'%232+with+x86-64+gcc+7.3',t:'0')),header:(),l:'4',m:25.93340060544904,n:'0',o:'',s:0,t:'0')),k:51.75925925925926,l:'3',n:'0',o:'',t:'0')),k:62.53602305475504,l:'2',m:100,n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4我们看到这个:
main: # @main
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], 0 # Default return value of 0
mov dword ptr [rbp - 8], 3
cmp dword ptr [rbp - 8], 3 # Is a > 3
jle .LBB0_2
mov dword ptr [rbp - 4], 5678 # Set return value to 5678
jmp .LBB0_5 # Go to common exit point .LBB0_5
.LBB0_2:
cmp dword ptr [rbp - 8], 3 # Is a == 3?
jne .LBB0_4
mov dword ptr [rbp - 4], 42 # Set return value to 42
jmp .LBB0_5 # Go to common exit point .LBB0_5
.LBB0_4:
jmp .LBB0_5 # Extraneous unoptimized jump artifact
# This is common exit point of all the returns from `main`
.LBB0_5:
mov eax, dword ptr [rbp - 4] # Use return value from memory
pop rbp
ret
正如我们所看到的,编译器生成了一个公共退出点来设置返回值(EAX) 从堆栈地址dword ptr [rbp-4]
。在代码的开头dword ptr [rbp-4]
显式设置为 0。在更简单的情况下,未优化的代码仍然生成该指令,但未使用。
如果您使用选项构建代码-ffreestanding
您应该看到默认返回值main
不再设置为 0。这是因为要求默认返回值为 0main
适用于托管环境而不是独立环境。