C语言 printf计算顺序和压栈顺序初探

发布于:2021-05-17 13:21:10

不同编译器产生的结果不同,本文测试环境为VS2013和VC6.


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??


先看以下代码:


int x = 3;
printf("%d%d%d%d%d%d", x, x++, x, ++x, x--, x);


VS2013输出结果:434434 ?


VC6输出结果:444433




那么问题来了:


(1)两个编译器printf的压栈顺序和计算顺序是怎样的?


(2)两个编译器如何对x++,++x处理的?


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??




VS2013反汇编代码



int x = 3;
010352CE mov dword ptr [x],3
printf("%d%d%d%d%d%d", x, x++, x, ++x, x--, x);
//x--
010352D5 mov eax,dword ptr [x]
010352D8 dword ptr [ebp-0D0h],eax //此时x = 3 ,将 x 的值存在 ebp-0d0h 这个相对地址中
010352DE mov ecx,dword ptr [x]
010352E1 sub ecx,1
010352E4 mov dword ptr [x],ecx
printf("%d%d%d%d%d%d", x, x++, x, ++x, x--, x);
//++x 直接在edx完成
010352E7 mov edx,dword ptr [x]
010352EA add edx,1
010352ED mov dword ptr [x],edx //x = 3
//x++
010352F0 mov eax,dword ptr [x]
010352F3 mov dword ptr [ebp-0D4h],eax //此时x = 3 ,将 x 的值存在 ebp-0d4h 这个相对地址中
010352F9 mov ecx,dword ptr [x]
010352FC add ecx,1
010352FF mov dword ptr [x],ecx
//压栈
01035302 mov esi,esp
01035304 mov edx,dword ptr [x]
01035307 push edx //4
01035308 mov eax,dword ptr [ebp-0D0h]
0103530E push eax //ebp-0D0h 地址中值为 3
0103530F mov ecx,dword ptr [x]
01035312 push ecx //4
01035313 mov edx,dword ptr [x]
01035316 push edx //4
01035317 mov eax,dword ptr [ebp-0D4h]
0103531D push eax //ebp-0D4h 地址中值为 3
0103531E mov ecx,dword ptr [x]
01035321 push ecx //4
01035322 push 103CA54h
01035327 call dword ptr ds:[1040184h]
结果 434434

VC6反汇编代码



int x = 3
00401028 movd word ptr [ebp-4],3
printf("%d%d%d%d%d%d",x,x++,x,++x,x--,x);
0040102F mov eax,dword ptr [ebp-4]
00401032 push eax //x = 3
//x--
00401033 mov ecx,dword ptr [ebp-4]
00401036 mov dword ptr [ebp-8],ecx //ebp-8 值为3
00401039 mov edx,dword ptr [ebp-8]
0040103C push edx //x = 3
//++x 在eax完成
0040103D mov eax,dword ptr [ebp-4]
00401040 add eax,1
00401043 mov dword ptr [ebp-4],eax
00401046 mov ecx,dword ptr [ebp-4]
00401049 push ecx //x = 4
0040104A mov edx,dword ptr [ebp-4]
0040104D push edx //x = 4
//x++
0040104E mov eax,dword ptr [ebp-4]
00401051 mov dword ptr[ebp-0Ch],eax //ebp-0ch 值为4
00401054 mov ecx,dword ptr[ebp-0Ch]
00401057 push ecx //x = 4
00401058 mov edx,dword ptr [ebp-4]
0040105B push edx //x = 4
0040105C push offset string"%d%d%d%d%d%d" (0042201c)
00401061 mov eax,dword ptr [ebp-4] //操作的是x值
00401064 add eax,1
00401067 mov dword ptr [ebp-4],eax
0040106A mov ecx,dword ptr [ebp-4] //同上
0040106D sub ecx,1
00401070 mov dword ptr [ebp-4],ecx
00401073 call printf (004010b0)
结果 444433



? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

由上反汇编代码,我们可以得出以下结论:


(1)两个编译器的压栈顺序相同:从右到左??计算顺序:因编译器而异,与压栈顺序无关。


(2)++x: 两编译器的处理方式相同,直接对寄存器进行值进行修改。


? ? ? ? ?x++:VS2013将x值先保存在某个相对地址中,再对现有x值累加计算,并继续其他运算,最后将存储的x值压入栈中;VC6直接将x值压入栈,并进行其他运算,压栈完成以后再对x值进行累加计算。


总结:结果不同的原因是由于两个编译器对x++处理方式的不同,在比较x++和++x的实现方式时,我们发现x++会在实现中产生一个副本,如果这个副本足够大的时候,在一定程度上会影响计算的效率,应此++x应该优先使用。




以上只是个人观点,有不合理之处欢迎各位指点。






相关推荐

最新更新

猜你喜欢