printf的(i++)和(++i)详解(编译器不同有差异)

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]  


你可能感兴趣的:(c语言进阶)