一、执行i++
和++i
时CPU在做什么
1.1 i++
C代码示例
#include <stdio.h>
int main() {
int i = 0;
i++;
printf("%d\n", i);
return 0;
}
第4行i++
对应的汇编代码如下:
add DWORD PTR [rbp-0x4],0x1 // 把内存单元[rbp-0x4]里面的值加1
1.2 i++
C代码示例
++i
C代码示例:
#include <stdio.h>
int main() {
int i = 0;
++i;
printf("%d\n", i);
return 0;
}
第4行++i
对应的汇编代码如下:
add DWORD PTR [rbp-0x4],0x1 // 把内存单元[rbp-0x4]里面的值加1
1.3 结论
是的,你没有看错,i++
和++i
对应的汇编代码是一模一样的。
也就是说,对于单独的一行C代码,i++;
和++i;
对CPU来说没有任何区别。
二、int a = i++
违反操作符优先级了吗?
C代码如下:
#include <stdio.h>
int main() {
int i = 0;
int a = i++;
printf("%d %d\n", a, i);
return 0;
}
这是初学者必学的知识点,打印的结果是0 1
。
这里的问题是:操作符++
的优先级高于=
,int a = i++;
就等于int a = (i++);
,因此会先计算(i++)
的值,i
变成了1
,再执行=
运算,所以理论上a
的值也应该是1
才对,那为什么实际结果却是0
呢?
《C程序设计语言》中是这么描述的:
表达式 ++n 先将 n 的值递增1,然后再使用变量 n 的值,而表达式 n++ 则是先使用变量 n 的值,然后再将 n 的值递增1。
注意了,这本书是C语言发明人合作编写的,上面这句话就是真理。
所以int a = i++
违反操作符优先级了吗?没有。因为C语言语法的优先级高于操作符优先级。
即,从语法上已经规定了int a = i++;
从C语言的角度等价于以下两行代码(从汇编角度不是这样的):
int a = i;
i++;
而int a = ++i;
从C语言的角度等价于以下两行代码(从汇编角度也是这样的):
i++;
int a = i;
三、执行int a = i++
和int a = ++i
时CPU在做什么?
3.1 执行int a = i++
时CPU在做什么
执行int a = i++
时的汇编代码如下(看注释):
mov eax,DWORD PTR [rbp-0x8] // 把[rbp-0x8]内存单元中的值(即0)存入eax寄存器
// 注:eax是rax寄存器的低32位
// 执行完这行指令之后,eax和rax的值都是0
lea edx,[rax+0x1] // edx = 1
mov DWORD PTR [rbp-0x8],edx // [rbp-0x8] = 1,即 i = 1
mov DWORD PTR [rbp-0x4],eax // [rbp-0x4] = 0,即 a = 0
可以看出,从汇编的角度来说,C语言int a = i++;
等价于以下三行代码:
int temp = i; // 先把 0 存起来(汇编代码第1行)
i++; // 再执行自增 (汇编代码第4和第5行)
a = temp; // 再给a赋值 (汇编代码第6行)
3.2 执行int a = ++i
时CPU在做什么
执行int a = ++i
时的汇编代码如下(看注释):
TODO
add DWORD PTR [rbp-0x8],0x1 // 把[rbp-0x8]内存单元中的值(即0)加1(从0变成1)
mov eax,DWORD PTR [rbp-0x8] // 把[rbp-0x8]内存单元中的值(即1)存入eax寄存器
mov DWORD PTR [rbp-0x4],eax // [rbp-0x4] = 1,即 a = 1
第1行执行了i++
,第2和第3行执行了a = 1
。
3.3 结论
int a = i++
比int a = ++i
多了一条汇编指令。
在执行int a = i++
时,出现了三个值:变量a
的值、变量i
的值、表达式i++
的值。变量i
的值从0
变成了1
,但是表达式i++
的值还是0
,所以最终a
的值是0
。
在执行int a = ++i
时,出现了三个值:变量a
的值、变量i
的值、表达式++i
的值。变量i
的值从0
变成了1
,表达式++i
的值也从0
变成了1
,所以最终a
的值是1
。
全文完