我试图更好地理解编译器如何为 undefined 表达式生成代码,例如对于以下代码:
int main()
{
int i = 5;
i = i++;
return 0;
}
这是 gcc 4.8.2 生成的汇编代码(优化关闭 -O0,我插入了自己的行号以供引用):
(gdb) disassemble main
Dump of assembler code for function main:
(1) 0x0000000000000000 <+0>: push %rbp
(2) 0x0000000000000001 <+1>: mov %rsp,%rbp
(3) 0x0000000000000004 <+4>: movl $0x5,-0x4(%rbp)
(4) 0x000000000000000b <+11>: mov -0x4(%rbp),%eax
(5) 0x000000000000000e <+14>: lea 0x1(%rax),%edx
(6) 0x0000000000000011 <+17>: mov %edx,-0x4(%rbp)
(7) 0x0000000000000014 <+20>: mov %eax,-0x4(%rbp)
(8) 0x0000000000000017 <+23>: mov $0x0,%eax
(9) 0x000000000000001c <+28>: pop %rbp
(10) 0x000000000000001d <+29>: retq
End of assembler dump.
执行此代码会产生 i
的值剩余值为 5(使用 printf()
语句验证)即 i
似乎永远不会增加。我知道不同的编译器会以不同的方式评估/编译未定义的表达式,这可能只是 gcc 的方式,即我可以用不同的编译器得到不同的结果。
关于汇编代码,据我了解:
忽略行 - 1-2 设置堆栈/基指针等。
第 3/4 行 - 5 的值是如何分配给 i
的.
谁能解释第 5-6 行发生了什么?看起来好像 i
将最终重新分配 5 的值(第 7 行),但是增量操作(后增量操作所需 i++
)是否只是被编译器在这种情况下放弃/跳过?
最佳答案
这三行包含你的答案:
lea 0x1(%rax),%edx
mov %edx,-0x4(%rbp)
mov %eax,-0x4(%rbp)
不会跳过增量操作。 lea
是增量,从%rax
中获取值并将增量值存储在%edx
中。 %edx
被存储,但随后被使用 %eax
中的原始值的下一行覆盖。
理解此代码的关键是了解 lea
的工作原理。它代表load effective address ,所以虽然它看起来像一个指针取消引用,但它实际上只是进行数学运算以获取 [whatever] 的最终地址,然后保留该地址,而不是 在 该地址的值。这意味着它可以用于任何可以使用寻址模式有效表达的数学表达式,作为数学操作码的替代方法。出于这个原因,它经常被用作获得乘法和添加到单个指令中的方法。特别是,在这种情况下,它用于在一条指令中增加值并将结果移动到不同的寄存器,其中 inc
会就地覆盖它。
关于c - 未定义表达式的汇编程序调试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30545954/