C语言中自增和自减操作符的使用有时真的令人头疼,如果不知道编译器如何处理它们,就无法正确预计计算的结果。
例如下面的例子:
int i=3;
int j=(++i)+(++i)+(++i);
第一次看到这段代码时,我觉得结果会是15,因为我想括号内的部分应该会被从左到右优先计算,那么结果就是4+5+6=15;
可是在Gcc中编译运行的结果却是16;在Visual Studio中编译运行的结果为:18;为什么会这样呢?
先说VS中的做法,它在编译时会觉得我们所加的三个括号内的内容优先级最高,那么它先计算括号内的,也就是将i自增了3次,变成了6,然后再想加,结果为18;
而在gcc中,应该是先计算了(++i)+(++i),即是先计算括号内的两次自增,然后再相加:5+5=10;最后计算:10+(++i),结果为16;
再看如下的示例:
int i = 3;
int j = 0;
j=++i+++i+++i;
这好像是一道面试题,初次看见,真不知道结果如何,编译后才发现编译器会报错。后来结合C编译器的“贪心法”才大概明白为何如此;以下是我个人的分析理解,是否正确没有考证;
C编译器贪心法的原则大概是:
编译器处理的每个“表达式”应该尽可能的多包含字符;
编译器从左到右一个一个尽可能多的读取字符;
当即将读入的字符不能和已读入的字符组成合法“表达式”为止。
据此分析上述示例:编译器读取到’+’,由于无法构成表达式,因此向后读取:’+’,还是无法构成,再读取:’i’,构成表达式了,由于贪心,想一次性多读取字符,故再读取:’+’,此时读取的表达式为:++i+,无法构成表达式,但是再读取一个有可能构成一个加法的表达式,因此再读取一个:’+’,此时读取的表达式为:++i++,这时候编译器就看不懂了,无论后面读取什么都无法构成有效的表达式,因此编译器就尝试着先来处理:++i++,于是它先计算:++i的值为4,然后表达式变成了:4++,这里问题就出来了,4是一个常量,如何自增呢??因此它会提示:表达式的左值必须是可修改的!(|error: lvalue required as increment operand)
同理,可以看如下的例子:
a+++b;
如上语句是等价于:a++ + b??还是a + ++b 呢??
同理,用贪心法分析:编译器读取到’a’后,继续读取:’+’,它会认为这是可行的,于是接着读取:’+’,这时的结果为:a++,自增表达式啊,合理的,于是接着读取:’+’,此时读入的为:a+++,因此编译器知道要先处理a++了,于是计算a++,此时a++计算后为一个值,表达式还能再读入一个字符,于是再读取一次:’b’,是合理的,就尝试着再读取,发现已经到了句末’;’,表示此语句已经结束,因此原句等价于:”a++ + b;”
一道面试题:
假如有如下语句,请判断它们合法吗?如果合法,请写入表达式的值:
int a = 4;
a+=(a++);
a+=(++a);
(a++) += (++a);
答案:
1.合法,值为9
2.合法,值为10
3.不合法
分析:
a+=(a++);表达式中先处理括号内的表达式,因为为后置++,所以“先使用a的值,再让a自增1”,等价于:4,a=a+1;因此括号内计算完成后a=5;
然后再计算a+=4;所以值为9;
(a++) += (++a);按照上面1的方法,先计算括号内的,所以应该是(a++),“先使用a的值,再让a自增1”,等价于:4,a=a+1=5; 然后再计算(++a),等价于:a=a+1=6,6; 最后计算:4+=6; 这个表达式显然不成立,4是一个常量值!所以编译器会报错:表达式必须是可修改的左值