我知道这个问题的变体已经在这里被问过多次,但我并不是在问两者之间有什么区别。只是想要一些帮助来理解这两种形式背后的组装。
我认为我的问题与whys比到what的差异。
我正在阅读Prata 的 C Primer Plus在处理增量运算符的部分++
和使用之间的区别i++
or ++i
作者说如果操作符自己使用的话,比如ego++;
我们使用哪种形式并不重要。
如果我们看一下以下代码的反汇编(使用 Xcode、Apple LLVM 版本 9.0.0 (clang-900.0.39.2) 编译):
int main(void)
{
int a = 1, b = 1;
a++;
++b;
return 0;
}
我们可以看到,实际上使用的形式并不重要,因为两者的汇编代码是相同的(两个变量都会在屏幕上打印出 2)。
初始化a
and b
:
0x100000f8d <+13>: movl $0x1, -0x8(%rbp)
0x100000f94 <+20>: movl $0x1, -0xc(%rbp)
装配用于a++
:
0x100000f9b <+27>: movl -0x8(%rbp), %ecx
0x100000f9e <+30>: addl $0x1, %ecx
0x100000fa1 <+33>: movl %ecx, -0x8(%rbp)
装配用于++b
:
0x100000fa4 <+36>: movl -0xc(%rbp), %ecx
0x100000fa7 <+39>: addl $0x1, %ecx
0x100000faa <+42>: movl %ecx, -0xc(%rbp)
然后作者指出,当运算符及其操作数是较大表达式的一部分时,例如在赋值语句中,使用前缀或后缀确实会产生影响。
例如:
int main(void)
{
int a = 1, b = 1;
int c, d;
c = a++;
d = ++b;
return 0;
}
这将打印1
and 2
for c
and b
, 分别。
And:
初始化a
and b
:
0x100000f46 <+22>: movl $0x1, -0x8(%rbp)
0x100000f4d <+29>: movl $0x1, -0xc(%rbp)
装配用于c = a++;
:
0x100000f54 <+36>: movl -0x8(%rbp), %eax // eax = a = 1
0x100000f57 <+39>: movl %eax, %ecx // ecx = 1
0x100000f59 <+41>: addl $0x1, %ecx // ecx = 2
0x100000f5c <+44>: movl %ecx, -0x8(%rbp) // a = 2
0x100000f5f <+47>: movl %eax, -0x10(%rbp) // c = eax = 1
装配用于d = ++b;
:
0x100000f62 <+50>: movl -0xc(%rbp), %eax // eax = b = 1
0x100000f65 <+53>: addl $0x1, %eax // eax = 2
0x100000f68 <+56>: movl %eax, -0xc(%rbp) // b = eax = 2
0x100000f6b <+59>: movl %eax, -0x14(%rbp) // d = eax = 2
显然,分配的汇编代码是不同的:
我的问题是:
- 这是为什么?
- 是什么决定了
c = a++;
需要两个寄存器而不是只有一个(ecx
例如)?