未定义表达式的汇编程序调试

use*_*467 10 c assembly x86-64 undefined

我试图更好地理解编译器如何为未定义的表达式生成代码,例如以下代码:

int main()
{
    int i = 5;
    i = i++;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是由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.
Run Code Online (Sandbox Code Playgroud)

执行此代码会导致值i保留为值5(通过printf()语句验证),即i似乎不会递增.我知道不同的编译器会以不同的方式评估/编译未定义的表达式,这可能只是gcc的方式,即我可以用不同的编译器获得不同的结果.

关于汇编代码,据我所知:

忽略行 - 1-2设置堆栈/基本指针等等.3/4 - 是如何分配5的值i.

任何人都可以解释5-6行的情况吗?它看起来好像i最终会重新分配值5(第7行),但是i++在这种情况下编译器简单地放弃/跳过增量操作(后增量操作所需)?

Leu*_*nko 8

这三行包含你的答案:

lea    0x1(%rax),%edx
mov    %edx,-0x4(%rbp)
mov    %eax,-0x4(%rbp)
Run Code Online (Sandbox Code Playgroud)

不跳过增量操作.lea是增量,从中获取值%rax并存储增量值%edx.%edx存储但由下一行使用原始值覆盖%eax.

他们理解这段代码的关键是知道如何lea工作.它代表加载有效地址,因此,虽然它看起来像一个指针引用,它实际上只是没有得到的最终地址所需要的数学[不论],然后保持地址,而不是值,在该地址.这意味着它可以用于任何可以使用寻址模式有效表达的数学表达式,作为数学操作码的替代.由于这个原因,它经常被用作获得乘法并添加到单个指令中的一种方法.特别是,在这种情况下,它用于递增值并将结果移动到一条指令中的不同寄存器,inc而不是就地覆盖它.


Jes*_*ter 5

第5-6行是i++.该lea 0x1(%rax),%edx是i + 1与mov %edx,-0x4(%rbp)写道回i.但是第7行,mov %eax,-0x4(%rbp)将原始值写回i.代码如下:

(4) eax = i
(5) edx = i + 1
(6) i = edx
(7) i = eax
Run Code Online (Sandbox Code Playgroud)