为什么不建议在C语言中连续使用自增自减运算符

相信很多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下编译运行,由于平台不同可能产生不同运行结果。

一条评论

  1. 额。。我们老师教的方法是,先加1再使用.因为只有一个变量c,所以res = (++c) + (++c);先算后面那个++c,c变成6,再算前面那个++c,c变成7,然后再拿来使用,这样就14了。

    Reply
  2. 今天使用这样一条语句,结果就来到了你的博客,呵呵,谢谢博主的分享。

    使用串口打印数组中的连个字节发现输出的都是第一个字节的内容,语句如下:
    hal_Printf1(“\r\na=%d b=%d”,*(pRate++),*(pRate++));

    我没有去看汇编指令,不知道这里编译器是怎么处理的。
    反正知道了最好不在一条语句里多次使用自增自减运算符。

    Reply

文章评论: