printf的入栈顺序和打印显示,一直是比较绕的东西,稍微总结了一下,发现有些地方和之前理解的是有出入的,记录一下,供以后自己查看。
下面的汇编是在vs2012中得到,不同编译器可能有差异,结果也可能不同。
第一种情况:
int i = 1;
printf("%d %d\n", i++, i++);
按从右到左的顺序,先压栈,首先将i = 1,入栈,然后i=2,将i=2入栈,i=3,因此输出结果是2 1 ,调用printf后,从栈中依次弹出2和1,这是按大家的理解的比较正常的方式。中间对于要入栈的变量,分别使用栈中空间暂存,本例中的ptr[ebp-0D0h]和ptr[ebp-0D4h]。
记下来看一下在vs2012中反汇编的结果:
int i = 1;
00D36EAE mov dword ptr [i],1 // ptr[i] = 1
printf("%d %d\n", i++, i++);
00D36EB5 mov eax,dword ptr [i]
00D36EB8 mov dword ptr [ebp-0D0h],eax // ptr[ebp-0D0h]=1
00D36EBE mov ecx,dword ptr [i]
00D36EC1 add ecx,1
00D36EC4 mov dword ptr [i],ecx
00D36EC7 mov edx,dword ptr [i] // i = i + 1 = 2
00D36ECA mov dword ptr [ebp-0D4h],edx // ptr[ebp-0D4h]=2
00D36ED0 mov eax,dword ptr [i]
00D36ED3 add eax,1
00D36ED6 mov dword ptr [i],eax // i = i + 1 = 3
00D36ED9 mov esi,esp
00D36EDB mov ecx,dword ptr [ebp-0D0h]
00D36EE1 push ecx // 将1压栈
00D36EE2 mov edx,dword ptr [ebp-0D4h]
00D36EE8 push edx // 将2入栈
00D36EE9 push 0D3CD0Ch
00D36EEE call dword ptr ds:[0D403CCh] // 调用printf
int i = 1;
printf("%d %d\n", ++i, ++i);
照之前的理解方式,从右向左入栈,则先计算++,依次将2和3入栈,弹出时依次弹出3和2,结果为3 2. 但是程序运行结果并不像我们所想,运行结果为3 3.
在vs2012中看下反汇编结果:
int i = 1;
00A76EAE mov dword ptr [i],1 // ptr[i] = 1
printf("%d %d\n", ++i, ++i);
00A76EB5 mov eax,dword ptr [i]
00A76EB8 add eax,1
00A76EBB mov dword ptr [i],eax // i = i + 1 = 2
00A76EBE mov ecx,dword ptr [i]
00A76EC1 add ecx,1
00A76EC4 mov dword ptr [i],ecx // i = i + 1 = 3
00A76EC7 mov esi,esp
00A76EC9 mov edx,dword ptr [i] // push i(3)
00A76ECC push edx
00A76ECD mov eax,dword ptr [i]
00A76ED0 push eax // push i(3)
00A76ED1 push 0A7CD0Ch
00A76ED6 call dword ptr ds:[0A803CCh] // call printf()
从汇编中可以看出,如果变量是形如++i,前自增的形式,先对所有变量进行自增操作,然后最后统一入栈。(
入栈时,直接从i的地址ptr[i]处取出数值)
混合情况:
int i = 1;
printf("%d %d %d %d\n", ++i, i++, ++i, i++);
综合前两种情况,输出结果为5 3 5 1.
反汇编:
int i = 1;
000D6EAE mov dword ptr [i],1
printf("%d %d %d %d\n", ++i, i++, ++i, i++);
000D6EB5 mov eax,dword ptr [i]
000D6EB8 mov dword ptr [ebp-0D0h],eax // ptr[ebp-0D0h] = 1
000D6EBE mov ecx,dword ptr [i]
000D6EC1 add ecx,1 // i = i + 1 = 2(i++)
000D6EC4 mov dword ptr [i],ecx
000D6EC7 mov edx,dword ptr [i]
000D6ECA add edx,1
000D6ECD mov dword ptr [i],edx // i = i + 1 = 3 (++i)
000D6ED0 mov eax,dword ptr [i]
000D6ED3 mov dword ptr [ebp-0D4h],eax // ptr[ebp-0D4h] = 3
000D6ED9 mov ecx,dword ptr [i]
000D6EDC add ecx,1
000D6EDF mov dword ptr [i],ecx // i = i + 1 = 4 (i++)
000D6EE2 mov edx,dword ptr [i]
000D6EE5 add edx,1
000D6EE8 mov dword ptr [i],edx // i = i + 1 = 5(++i)
000D6EEB mov esi,esp
000D6EED mov eax,dword ptr [ebp-0D0h]
000D6EF3 push eax // push 1
000D6EF4 mov ecx,dword ptr [i]
000D6EF7 push ecx // push 5
000D6EF8 mov edx,dword ptr [ebp-0D4h]
000D6EFE push edx // push 3
000D6EFF mov eax,dword ptr [i]
000D6F02 push eax // push 5
000D6F03 push 0DCD0Ch
000D6F08 call dword ptr ds:[0E03CCh]