关于 printf的深入讨论

 

    int a = 15;

    printf("%d %d\n", ++a, a++);

    老问题了,也没什么好说的,但是顺序点涉及到参数的情况还没仔细考虑过,所以分析下其内部原理。

    我本来的考虑是按照 cdecl 压栈次序,a++ 先压入,为 15,此后++发生副作用,a 变为 16,然后压入 ++a,a 先自增,为 17,所以输出结果为 17 15。虽然心里忐忑了一下,但也就这样自欺欺人了一会,用 gcc 验证无误,然后 vc6 就给了我一棒子,它的输出为 16 15。

    说实话参数里副作用的顺序点我没有研究过,所以导致了这个结果,于是好好看了下他们生产的汇编码:

    VC6相应汇编代码:

5:        int a = 15;
00401028   mov         dword ptr [ebp-4],0Fh
6:
7:        printf("%d %d\n", ++a, a++);
0040102F   mov         eax,dword ptr [ebp-4]
00401032   mov         dword ptr [ebp-8],eax
00401035   mov         ecx,dword ptr [ebp-8]
00401038   push        ecx
00401039   mov         edx,dword ptr [ebp-4]
0040103C   add         edx,1
0040103F   mov         dword ptr [ebp-4],edx
00401042   mov         eax,dword ptr [ebp-4]
00401045   push        eax
00401046   push        offset string "%d %d\n" (00422020)
0040104B   mov         ecx,dword ptr [ebp-4]
0040104E   add         ecx,1
00401051   mov         dword ptr [ebp-4],ecx
00401054   call        printf (004010a0)
00401059   add         esp,0Ch

 

    可以看出,在 0x00401038 处,压入了第三个参数为 15,0x0040103C 处 a++ 的副作用发生,a 变为 16;0x00401045 处压入第二个参数为 16,直到 0x0040104E 处 ++a 的副作用才发生,输出结果为 16 15。我感觉这里的函数参数的逗号分隔符并不是逗号表达式,所以不是一个顺序点,导致 ++a 在这里的副作用不一定发生,所以此时 vc 没有增加 a 的值,是不是这样呢?难道对于函数参数,顺序点在函数结束处[待研究]?

    再来看看 gcc 4.3 的情况,以下是同样程序它的汇编码:

0x00401146 <main+22>:   movl   $0xf,-0x8(%ebp)
0x0040114d <main+29>:   mov    -0x8(%ebp),%eax
0x00401150 <main+32>:   addl   $0x1,-0x8(%ebp)
0x00401154 <main+36>:   addl   $0x1,-0x8(%ebp)
0x00401158 <main+40>:   mov    %eax,0x8(%esp)
0x0040115c <main+44>:   mov    -0x8(%ebp),%eax
0x0040115f <main+47>:   mov    %eax,0x4(%esp)
0x00401163 <main+51>:   movl   $0x4020a0,(%esp)
0x0040116a <main+58>:   call   0x4011d8 <printf>

    可以看出,在 0x00401150 和 0x00401154 处 a 自增了 2 次,0x00401158 处压入 printf 的第三个参数为 15,0x0040115f 处压入第二个参数,而此时经过 0x0040115c,第二个参数已经为 17 了,所以结果为 17 15。

    虽然是支末问题,不用纠缠,但是仍然引出了很多不确定的东西,当是学习了一次吧。

 

你可能感兴趣的:(c,String,汇编,gcc)