相信很多coder在学习C语言(包括C++)的过程中都听说过这样的建议:慎用自增自减运算符。
这是因为,在函数参数或者表达式中多次调用自增自减运算符很可能产生“不可预知的结果”。究竟有多不可预知呢?请看这样一个程序
#include
int main()
{
int c, res;
c = 5;
res = (++c) + (++c);
printf("%d %d", c, res);
return 0;
}
首先定义变量c和res,给c赋值5,然后将(++c)+(++c)的值赋值给res,最后输出c和res。按照正常的思路:第一个(++c)先将c自增为6然后返回这个值6,第二个(++c)再将c自增为7并返回7,最后将6和7相加得到13赋给res。这样的话输出应该是这样的:
7 13
程序看似简单,但运行结果却很神奇:
7 14
c的输出和我们预想的一样,经过两次自增运算,5变成了7。但是,res的值就令人费解了,为什么会输出14呢?经过Simollus对程序反汇编的研究之后,发现了这样的问题。
这是反汇编的一个片段,注释是为方便理解改写成C语言样式:
6: c = 5;
00401028 mov dword ptr [ebp-4],5 //c = 5;
7: res = (++c) + (++c);
0040102F mov eax,dword ptr [ebp-4] //eax = c;
00401032 add eax,1 //eax +=1;
00401035 mov dword ptr [ebp-4],eax //c = eax;
00401038 mov ecx,dword ptr [ebp-4] //ecx = c;
0040103B add ecx,1 //ecx += 1;
0040103E mov dword ptr [ebp-4],ecx //c = ecx;
00401041 mov edx,dword ptr [ebp-4] //edx = c;
00401044 add edx,dword ptr [ebp-4] //edx += c;
00401047 mov dword ptr [ebp-8],edx //res = edx
可以发现,C语言的编译器犯了一个小小的“错误”,没有意识到c的值已经改变,在做最后的加法运算时调用了两次同样的c,导致结果成为7+7=14。当然,不同的编译器可能会犯不同的错误,也有可能不犯错误。
在这篇文章中有对于这种现象更为详细的例子分析。尽管我们可以了解并预知程序在不同编译器下会产生不同的结果,但是我认为最好的解决办法是:
不要连续使用自增自减运算符
注:本文所有程序均为在Visual C++ 6.0下编译运行,由于平台不同可能产生不同运行结果。