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

Assembler debug of undefined expression

我试图更好地理解编译器如何为 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   [=11=]x5,-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    [=11=]x0,%eax
(9) 0x000000000000001c <+28>:   pop    %rbp
(10) 0x000000000000001d <+29>:  retq   
End of assembler dump.

执行此代码导致 i 的值保持在 5 的值(使用 printf() 语句验证)即 i 似乎永远不会增加。我知道不同的编译器会以不同的方式 evaluate/compile 未定义的表达式,这可能只是 gcc 的方式,即我可以用不同的编译器得到不同的结果。

关于汇编代码,据我理解:

忽略行 - 1-2 设置 stack/base 指针等。 第 3/4 行 - 5 的值如何分配给 i.

谁能解释第 5-6 行发生了什么?看起来好像i最终会被重新赋值为5(第7行),但是是自增操作(post自增操作需要i++) 只是 abandoned/skipped 编译器在这种情况下?

第5-6行,就是i++lea 0x1(%rax),%edxi + 1mov %edx,-0x4(%rbp) 将其写回到 i。但是第 7 行,mov %eax,-0x4(%rbp) 将原始值写回 i。代码如下:

(4) eax = i
(5) edx = i + 1
(6) i = edx
(7) i = eax

这三行包含你的答案:

lea    0x1(%rax),%edx
mov    %edx,-0x4(%rbp)
mov    %eax,-0x4(%rbp)

增量操作没有被跳过。 lea是增量,从%rax中取值并将增量值存储在%edx中。 %edx 被存储,但随后被使用 %eax 中原始值的下一行覆盖。

理解此代码的关键是了解 lea 的工作原理。它代表 load effective address,所以虽然它看起来像一个指针解引用,但它实际上只是进行数学计算以获得 [whatever] 的最终地址,然后保留地址,而不是值 那个地址。这意味着它可以用于任何可以使用寻址模式有效表达的数学表达式,作为数学操作码的替代方法。出于这个原因,它经常被用作获得乘法和添加到单个指令中的方法。特别是,在这种情况下,它用于在一条指令中递增值并将结果移动到不同的寄存器,其中 inc 会就地覆盖它。