生活的天平本不平衡,只有通过努力改变其偏向。

C语言中的i++和++i的逆向分析

2008-03-14

公司同事在讨论一个比较变态的问题,关于前自增和后自增的运行结果。
这种题目经常出现C语言面试试题当中。我也不知道结果,就作了一下逆向,没想到受益不小。
逆向比较简单,高手飘过…..,随便BS一下出这样面试题目的人,至于为什么我后面再作分析。

工具:Vc6.0+IDA
平台:WinXP + SP2

1
2
3
4
5
6
int i=5,j=5;
int p,q;
p=(i++)+(i++)+(i++);
q=(++j)+(++j)+(++j);  
 
printf("i=%d,j=%d,p=%d,q=%d\n",i,j,p,q);

Vc6.0的Debug模式下输出:
i=8,j=8,p=15,q=22

.text:00401010 _main_0         proc near ; CODE XREF: _mainj
.text:00401010
.text:00401010 var_50          = byte ptr -50h
.text:00401010 var_10          = dword ptr -10h
.text:00401010 var_C           = dword ptr -0Ch
.text:00401010 var_8           = dword ptr -8
.text:00401010 var_4           = dword ptr -4
.text:00401010
.text:00401010                 push ebp
.text:00401011                 mov ebpesp
.text:00401013                 sub esp, 50h
.text:00401016                 push ebx
.text:00401017                 push esi
.text:00401018                 push edi
.text:00401019                 lea edi, [ebp+var_50]
.text:0040101C                 mov ecx, 14h
.text:00401021                 mov eax, 0CCCCCCCCh
.text:00401026                 rep stosd
.text:00401028                 mov [ebp+var_4], 5  ; 将i赋值成5
.text:0040102F                 mov [ebp+var_8], 5  ; 将j赋值成5
.text:00401036                 mov eax, [ebp+var_4]
.text:00401039                 add eax, [ebp+var_4]
.text:0040103C                 add eax, [ebp+var_4]
.text:0040103F                 mov [ebp+var_C], eax ; 用i连加三次,将结果保存到P中。
.text:00401042                 mov ecx, [ebp+var_4]
.text:00401045                 add ecx, 1
.text:00401048                 mov [ebp+var_4], ecx
.text:0040104B                 mov edx, [ebp+var_4]
.text:0040104E                 add edx, 1
.text:00401051                 mov [ebp+var_4], edx
.text:00401054                 mov eax, [ebp+var_4]
.text:00401057                 add eax, 1
.text:0040105A                 mov [ebp+var_4], eax ; 对i连加三次1,可以看出(i++)+(i++)+(i++)是将i的三个初始值连加三次,
.text:0040105A                                         ; 保存到结果,然后再作i自增
.text:0040105D                 mov ecx, [ebp+var_8]
.text:00401060                 add ecx, 1
.text:00401063                 mov [ebp+var_8], ecx
.text:00401066                 mov edx, [ebp+var_8]
.text:00401069                 add edx, 1
.text:0040106C                 mov [ebp+var_8], edx ; 对j先加两次1,此时的j=7
.text:0040106F                 mov eax, [ebp+var_8]
.text:00401072                 add eax, [ebp+var_8] ; 用eax作暂存,对当前的j值进行两次相加
.text:00401075                 mov ecx, [ebp+var_8]
.text:00401078                 add ecx, 1
.text:0040107B                 mov [ebp+var_8], ecx ; j自增1
.text:0040107E                 add eax, [ebp+var_8]
.text:00401081                 mov [ebp+var_10], eax ; 将此时的j与eax相加,并将结果保存到q中
.text:00401084                 mov edx, [ebp+var_10]
.text:00401087                 push edx
.text:00401088                 mov eax, [ebp+var_C]
.text:0040108B                 push eax
.text:0040108C                 mov ecx, [ebp+var_8]
.text:0040108F                 push ecx
.text:00401090                 mov edx, [ebp+var_4]
.text:00401093                 push edx ; 四个参数入栈,调用printf
.text:00401094                 push offset Format   ; ”i=%d,j=%d,p=%d,q=%d\n”
.text:00401099                 call _printf
.text:0040109E                 add esp, 14h
.text:004010A1                 xor eaxeax ; 调用者清栈
.text:004010A3                 pop edi
.text:004010A4                 pop esi
.text:004010A5                 pop ebx ; 恢复寄存器值
.text:004010A6                 add esp, 50h
.text:004010A9                 cmp ebpesp
.text:004010AB                 call __chkesp
.text:004010B0                 mov espebp
.text:004010B2                 pop ebp
.text:004010B3                 retn
.text:004010B3 _main_0         endp

对照结果,我们分析一下前自增和后自增的过程:
前自增:
1.先用i的初始值相加三次,得到P的值。
2.然后再对i进行三次递增。

后自增:
1.先将j进行两递增
2.再对j进行两次相加,将值保存在EAX中
3.再对j作一次自增
4.再用当前的j值和EAX值相加得出q值

文章开头为什么BS出这样题的人呢?我们将编译模式设成Release模式
运行一下,发现结果和Debug模式下的不一样了。
结果是:i=8,j=8,p=15,q=24
奇怪同样的代码在Debug和Release下怎么这么大的区别呢?

_main proc near
push 18h
push 0Fh
push 8
push 8               ; 编译器直接将结果给优化。
push offset aIDJDPDQD ; ”i=%d,j=%d,p=%d,q=%d\n”
call sub_401020
add esp, 14h
xor eaxeax
retn
_main endp

呵呵,很简洁吧。原来编译器在编译过程中作了很大的优化,直接将结果给生成好。
而且两种模式下的产生的结果不一样,这正是我为什么要BS出这类题目的人。好好的代码为什么要这样写,产生了很大的歧义。这种写法的代码真的没有实际意义。如果在Debug下产生了相要的结果,等项目发布时,发现了问题,要花多大的代价才能找出这个Bug?
所以代码不能一味的求简洁,要保证结果的正确性才是最重的。

作者:lonkil | 分类目录:本站原创编程开发 | 标签:

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>