这既不是错误/功能,也不是编译器优化的副作用。
反汇编显然是非优化构建的输出(i 写成
到foo+6 中的堆栈,然后在foo+13 中从堆栈重读一步)。
虽然在这种情况下 C 和 C++ 的汇编输出相同,但调试符号输出却略有不同。 i 的范围在 C++ 中受到更多限制。我只能推测原因。我猜这与 C++ 中的作用域更复杂(想想构造函数、析构函数、异常)这一事实有关,因此 gcc 的 C++ 部分在作用域上比 gcc 的 C 部分更严格。
详情
(我在 32 位版本上检查了所有内容,但在使用 gcc 4.8 和 gdb 7.6 的 64 位 Linux 上进行了检查。虽然 Windows 上的一些细节会有所不同,但我希望一般机制是相同的)
请注意,我的地址不同。
(gdb) disas foo
Dump of assembler code for function foo:
0x080483ed <+0>: push %ebp
0x080483ee <+1>: mov %esp,%ebp
0x080483f0 <+3>: sub $0x10,%esp
0x080483f3 <+6>: movl $0x5,-0x4(%ebp)
0x080483fa <+13>: mov -0x4(%ebp),%eax
0x080483fd <+16>: leave
0x080483fe <+17>: ret
End of assembler dump.
从技术上讲,foo+0 和 foo+1 是函数序言,foo+3 到 foo+13 是函数体,foo+16 和 foo+17 是函数尾声。所以只有foo+3 到foo+13 代表{ 和} 之间的代码。我会说 C++ 版本更正确的说法是 i 在函数体之前和之后超出范围。
要查看这实际上是调试符号的问题,您可以使用maintenance print symbols output_file_on_disk 转储 gdb 的调试结构内部。对于 C,它看起来像:
块 #000,对象位于 0x1847710,0x80483ed..0x804840e 中的 1 个符号/存储桶
int foo();块对象 0x18470d0, 0x80483ed..0x80483ff
int main(int, char **);块对象 0x18475d0、0x80483ff..0x804840e 部分 .text
块 #001,对象位于 0x18476a0 下 0x1847710,1 syms/buckets 在 0x80483ed..0x804840e
typedef int int;
typedef char 字符;
block #002,0x18476a0 下 0x18470d0 处的对象,
0x80483ed..0x80483ff 中的 1 个 syms/buckets,函数 foo
int i;在运行时计算
块 #003,0x18476a0 下 0x18475d0 处的对象,0x80483ff..0x804840e 中的 2 个 syms/buckets,函数 main
整数 argc;在运行时计算
字符 **argv;在运行时计算
虽然这是 C++
块 #000,0x1a3c790 处的对象,0x80483ed..0x804840e 中的 1 个符号/存储桶
int foo();块对象 0x1a3c0c0, 0x80483ed..0x80483ff
int main(int, char**);块对象 0x1a3c640, 0x80483ff..0x804840e 部分 .text
块 #001,0x1a3c720 下 0x1a3c790 下的对象,0x80483ed..0x804840e 中的 1 个符号/存储桶
typedef int int;
typedef char 字符;
block #002,0x1a3c0c0 下 0x1a3c720 下的对象,
0x80483ed..0x80483ff 中的 0 个 syms/buckets,函数 foo()
block #003,0x1a3c050 下 0x1a3c0c0 的对象,
0x80483f3..0x80483fd 中的 1 个 syms/buckets
int i;在运行时计算
块 #004,0x1a3c640 下 0x1a3c720 下的对象,0x80483ff..0x804840e 中的 2 个符号/存储桶,函数 main(int, char**)
整数 argc;在运行时计算
字符 **argv;在运行时计算
因此,C++ 代码的调试符号区分了整个函数(块#002)和函数体的范围(块#003)。这会产生您的观察结果。
(看到这真的不是 gdb 只是处理错误,您甚至可以在 Linux 上使用 objdump 或在 Windows 上使用 dumpbin 分析二进制文件。我在 Linux 上完成了它,实际上它是 DWARF 调试符号不同:-))