这个C++代码中发生了什么?

fR0*_*DDY 60 c++

任何人都可以解释这个C++代码的内容.它在Linux上编译并执行正常.

#include <iostream>
using namespace std;
int main = ( cout << "Hello world!\n", 195 );
Run Code Online (Sandbox Code Playgroud)

小智 68

数字"195"是x86上RET指令的代码.

C++编译器(在我的情况下是gcc)无法识别"main"未被声明为函数.编译器只看到有"主"符号,并假设它引用了一个函数.

C++代码

int main = ( cout << "Hello world!\n", 195 );
Run Code Online (Sandbox Code Playgroud)

正在初始化文件范围的变量.此初始化代码在C/C++环境调用main()之前执行,但在初始化"cout"变量之后执行.初始化打印"Hello,world!\n",并将变量"main"的值设置为195.完成所有初始化后,C/C++环境调用"main".程序立即从此调用返回,因为我们在"main"的地址处放置了一条RET指令(代码195).

示例GDB输出:

$ gdb ./a
(gdb) break _fini
Breakpoint 1 at 0x8048704
(gdb) print main
$1 = 0
(gdb) disass &main
Dump of assembler code for function main:
   0x0804a0b4 <+0>:     add    %al,(%eax)
   0x0804a0b6 <+2>:     add    %al,(%eax)
End of assembler dump.
(gdb) run
Starting program: /home/atom/a 
Hello world!

Breakpoint 1, 0x08048704 in _fini ()
(gdb) print main
$2 = 195
(gdb) disass &main
Dump of assembler code for function main:
   0x0804a0b4 <+0>:     ret    
   0x0804a0b5 <+1>:     add    %al,(%eax)
   0x0804a0b7 <+3>:     add    %al,(%eax)
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)

  • +1用于深入解释为什么**起作用. (8认同)
  • @ new123456:为什么它*有时*工作.如上所述,在OS X上它崩溃,标准指定这不是一个有效的C++程序. (3认同)
  • 向后兼容性,特别是与执行JIT的软件(javascript解释器,旧的OpenGL实现等) (2认同)

Ale*_*x B 39

它不是一个有效的C++程序.事实上,在打印"Hello World"之后,它在Mac OSX上崩溃了.

反汇编显示main是一个静态变量,它有初始化器:

global constructors keyed to main:
0000000100000e20    pushq   %rbp
0000000100000e21    movq    %rsp,%rbp
0000000100000e24    movl    $0x0000ffff,%esi
0000000100000e29    movl    $0x00000001,%edi
0000000100000e2e    leave
0000000100000e2f    jmp __static_initialization_and_destruction_0(int, int)
Run Code Online (Sandbox Code Playgroud)

为什么打印"Hello World"?

您看到打印出"Hello World"的原因是因为它在静态初始化main静态整数变量期间运行.在C++运行时甚至尝试调用之前调用静态初始值设定项main().当它发生时,它会崩溃,因为main它不是一个有效的函数,在可执行文件的数据部分只有一个整数195.

其他答案表明这是一个有效的ret指令,它在Linux中运行良好,但它在OSX上崩溃,因为默认情况下该部分被标记为不可执行.

为什么C++编译器不能告诉main()不是函数并且因链接器错误而停止?

main()有C链接,所以链接器不能区分符号的类型.在我们的例子中,_main驻留在数据部分.

start:
0000000100000eac    pushq   $0x00
0000000100000eae    movq    %rsp,%rbp
...
0000000100000c77    callq   _main ; 1000010b0
0000000100000c7c    movl    %eax,%edi
0000000100000c7e    callq   0x100000e16 ; symbol stub for: _exit
0000000100000c83    hlt
...
; the text section ends at 100000deb
Run Code Online (Sandbox Code Playgroud)

  • 有网站上的[地方](http://d.hatena.ne.jp/qnighy/20090418/1240064403)声称这应该适用于IA32体系结构,因为`195`是`0xC3`,即`RET `指示.我看起来仍然很奇怪,但...... (12认同)
  • @FrédéricHamidi:在发出盲调的情况下,它可能是由链接器而不是编译器发出的.连接器可能非常盲目. (2认同)

Jam*_*nze 5

这不是一个合法的程序,但我认为标准对于是否需要诊断或者是未定义的行为有点模棱两可.(从实施的质量来看,我期待诊断.)

  • @SteveJessop如果您为独立环境进行编译,当然,它是所有实现定义的.但是编译器应该知道这一点(因为它必须知道是否特殊处理`main`),并且如果它特别处理函数`main`(没有修改等)以便从` crt0`,然后它知道全局命名空间中的`main`是特殊的,并且可以为示例代码生成错误. (2认同)