++i和i++的功能
void test(int i)
{
//假设此时传入参数i = 0
int a = ++i;//执行完这行代码后,a = 1,i = 1
int b = i++;//b = 1,i = 2
}
简而言之,++i是先+1再2赋值,i++是赋值完再+1
从汇编角度看两者的差异
准备工作
- 编写一段功能简单的程序test.c 如下:
#include<stdio.h>
void testi(int i){
int a = i++;
}
void testj(int j){
int a = ++j;
}
int main()
{
int i = 0;
int j = 0;
testi(i);
testj(j);
}
- 使用gcc编译它
gcc -o test test.c
- 使用objdump命令反汇编并重定向到.asm文件中
objdump -d test > test.asm
分析汇编代码
由上一步骤得到的.asm文件过长,此处不再一一展示,只选取最重要的部分
0000000000001129 <testi>:
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: 89 7d ec mov %edi,-0x14(%rbp)
1134: 8b 45 ec mov -0x14(%rbp),%eax
1137: 8d 50 01 lea 0x1(%rax),%edx
113a: 89 55 ec mov %edx,-0x14(%rbp)
113d: 89 45 fc mov %eax,-0x4(%rbp)
1140: 90 nop
1141: 5d pop %rbp
1142: c3 retq
0000000000001143 <testj>:
1143: f3 0f 1e fa endbr64
1147: 55 push %rbp
1148: 48 89 e5 mov %rsp,%rbp
114b: 89 7d ec mov %edi,-0x14(%rbp)
114e: 83 45 ec 01 addl $0x1,-0x14(%rbp)
1152: 8b 45 ec mov -0x14(%rbp),%eax
1155: 89 45 fc mov %eax,-0x4(%rbp)
1158: 90 nop
1159: 5d pop %rbp
115a: c3 retq
应当明白,程序中的栈是向上增长的,即栈底的内存地址大于栈顶的内存地址
此处说明几个寄存器的作用:
- %rsp:栈指针
- %edi:传入的第一个参数所存储的通用寄存器。程序例子中只有一个参数,因此使用这个寄存器
- %rax:存放临时变量
在汇编代码中,%eax就是变量a,又因为栈的向上增长,因此这里对栈基址做减法
mov %edi,-0x14(%rbp)
此时传入的参数就存储在-0x14(%rbp)
这个内存地址中。
对比testi和testj,可以看到testi部分多了一个%edx寄存器,由于在+1前需要先赋值给变量a,因此%edx用于存储+1后的数据,再回送到-0x14(%rbp)
中。而在testj中,由于是先+1再赋值,因此可以直接对着内存地址-0x14(%rbp)
+1,再使用mov指令将其传送给%eax。最后,两个函数都将%eax的值传送给内存地址-0x4(%rbp)
处。
可以看到,在汇编代码中,i++访问了5次内存,比++i多了1次。因此i++比++i更加耗时。
但是,如果仅仅做i++或者仅仅做++i的操作而不将其赋值给任何变量,在汇编代码中,两者的代码是一样的,可以简单编写程序求证。