很多人都知道c语言中的printf语句是从右到左输出的,那么对于下面两个语句到底是怎样执行的呢?
int i = 10, j = 10;
printf("%d %d\n", i, i++)
printf("%d %d\n", j, ++j)
很多人都知道i++和++j的区别,但编译器到底是怎么做的呢,我们可以在上面语句对应的汇编代码中找到答案,上面两行代码的
printf("%d %d\n", i, i++);
001791EF mov eax,dword ptr [i]
001791F2 mov dword ptr [ebp-0DCh],eax
001791F8 mov ecx,dword ptr [i]
001791FB add ecx,1
001791FE mov dword ptr [i],ecx
00179201 mov esi,esp
00179203 mov edx,dword ptr [ebp-0DCh]
00179209 push edx
0017920A mov eax,dword ptr [i]
0017920D push eax
0017920E push 17CD08h
00179213 call dword ptr ds:[1801D8h]
00179219 add esp,0Ch
0017921C cmp esi,esp
0017921E call __RTC_CheckEsp (017132Fh)
printf("%d %d\n", j, ++j);
00179223 mov eax,dword ptr [j]
00179226 add eax,1
00179229 mov dword ptr [j],eax
0017922C mov esi,esp
0017922E mov ecx,dword ptr [j]
00179231 push ecx
00179232 mov edx,dword ptr [j]
00179235 push edx
00179236 push 17CD08h
0017923B call dword ptr ds:[1801D8h]
00179241 add esp,0Ch
00179244 cmp esi,esp
00179246 call __RTC_CheckEsp (017132Fh)
对printf("%d %d\n", i, i++)进行一步步分析:
001791EF mov eax,dword ptr [i]
001791F2 mov dword ptr [ebp-0DCh],eax
先将i的值保存在了栈中(ebp-0DCh表示系统栈中的一块地址,ebp存储的是栈底地址,系统栈是按地址从大到小的方向生长的,即栈底地址是最大的)
001791F8 mov ecx,dword ptr [i]
001791FB add ecx,1
001791FE mov dword ptr [i],ecx
实现对i加1的操作
00179203 mov edx,dword ptr [ebp-0DCh]
00179209 push edx
将ebp-0DCh中的值存放在栈中,即将i++入栈
0017920A mov eax,dword ptr [i]
0017920D push eax
将i入栈
从上面入栈的操作可以看出,先对i++处理,然后处理i,是按从右到左的顺序处理的
对printf("%d %d\n", j, ++j)进行一步步分析:
00179223 mov eax,dword ptr [j]
00179226 add eax,1
00179229 mov dword ptr [j],eax
由于是++j的操作,所以直接对j进行加1的操作,不用将j存在一个临时变量中
0017922E mov ecx,dword ptr [j]
00179231 push ecx
将j入栈,此时的j是进行++后操作的j,即使右边的j
00179232 mov edx,dword ptr [j]
00179235 push edx
将j入栈,是左边的j
上面参数入栈的顺序也是按从右到左的顺序处理的
这里的关键是对i++和++j的理解,对i++编译器是先将其存储在一个临时空间中,然后将其加1,i++的返回值就是i原先的值,因此对于需要i++返回值的对象,其使用的是临时空间中的值;而++j首先就是将j的值加1,然后返回的是加1后的j,因此需要++j返回值的对象就是使用的新的j值
//下面的两行代码,i++和++i执行完之后i都已经加1了,只是它们的返回值不同,i++返回的是原来的i值,++i返回的是新的i值
i++;
++i;
//下面两行代码i的值是一样的,j的值不同,j = i++中j是等于原来的i值,j = ++i中j是等于新的i值
j = i++;
j = ++i;