++和--运算

来源:岁月联盟 编辑:zhu 时间:2009-02-13
++是C++的自增运算符,作用是使变量自加1;--是自减运算符,作用是使变量自减1。++和--有两种用法,一种是前缀用法,一种是后缀用法。前缀用法如:++i、--i ,后缀用法如i++、i--,前缀用法跟后缀用法的差别在于前缀时++i的值为完成i加1后的值,--i为完成i减1后的值。例如:假设i的初值为3,执行cout<<++i<<endl;输出结果为4,而执行cout<<i++<<endl;输出结果为3。--运算符同理。这是世人皆知的常识,我们不再讨论,现在我们来讨论一点有趣的东西,看如下代码:



#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<(i++)+(i++)+(i++)<<endl;

cout<<i<<endl;

return 0;

}

问,第一次和第二次输出的结果分别是多少?



有人说,是12和6。理由是,表达式从左至右开始计算,因为第一个括号内++运算符是后缀用法,i的初值为3,所以,第一个括号的值是3,计算完第一个括号之后,i自加1,变成4,然后计算第二个括号,第二个括号里的++也是后缀用法,所以,值为4,执行完第二个括号后,i再加1,变成5,接下计算第三个括号,第三个括号里的++也是后缀用法,所以,第三个括号的值为5,然后计算第三个括号相加的和,即3+4+5=12。这个理由看起来不错,似乎应当是这样。然而,运行结果却让人大跌眼镜,竟然是9和6。这是怎么回事呢?说起来也很简单,这是因为很多编译系统规定,在遇到一条计算表达式中同时出现若干i++、i--的情况时,在当前语句中并不执行i的自增和自减,i的初值是多少,i++和i--的值就是多少,当这条表达式执行完成之后,再将i连续自加或自减若干次。

再看如下代码:



#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<(++i)+(++i)+(++i)<<endl;

cout<<i<<endl;

return 0;

}

问,第一次和第二次输出的结果分别是多少?

有人说,结果应该是4+5+6=15和6。理由我想大家都想明白,我就不多说了。还有人总结了上例的经验,认为,输出结果应该是9和6。我们来运行一下这个程序,看看谁说得对……

好了,运行结果出来了,不过这不是什么好结果,可能很多人看完会抓狂,结果尽然是神鬼莫测的18和6。为什么呢?道理跟上例差不多,那就是很编译系统规定,连续多个前缀式++和--运算符出现在同一个运算表达式中时,先将变量连续自加或自减N次,然后判定++i的值为i+N。



为了验证上面的说法,请看下面的代码:



#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<(++i)+(i++)+(++i)<<endl;

cout<<i<<endl;

return 0;

}

按照我们上面的推测,第一个输出语句应当是这样执行的:首先,扫描整条运算表达式(++i)+(i++)+(++i),发现有两处++的前缀式用法,于是,将i连续自加两次,然后开始计算表达式,第一个括号是++i,判定为5,第二个括号是i++,判定值为5,第三个括号是++i判定值为5,最后,计算结果5+5+5=15。因为表达式中有一个i++,所以执行计算完之后将i的值再自加1,变为6。

运行程序,验证一下,果然,结果就是15和6。



下面在来讨论一下网上很多C++论坛里讨论得很多的int i=3;问++i+++i+i++的值是多少的问题。

我看到CSDN里也有人在讨论这个问题,很多人在回帖,答案似乎多种多样,有说是12的,有说是18的,更有说是9的,更有一条回帖十分搞笑——“答案是×××,这是很早以前我的一个很牛×的老师教我的解法得出的结果”。我很无语。学过编译原理的人都知道,“++i+++i”这一段根本就无法解析,编译系统从左至右扫描整条语句,先遇到++i,判断出来是一个i的前缀自加运算,然后接着扫描,遇到一个+,+是一个二目运算符,它的左边已经有一个运算数++i了,系统就向右搜索第二个运算数,又遇到一个+,++比+的运算级别要高,这时,编译系统就将两个+看成一个整体来处理,既然是++,编译系统就认定,肯定它的左边或右边有一个变量,编译系统先搜索左边,发现有一个 i,是个变量,于是它就将i和其后的++组合起来,这时问题就发生了,也就是说第一个i被编译系统绑架到它后面的++那里去了,那么i前面的++是个什么东西呢?编译系统是无法搞明白的,它会倒回去重新搜索++前面是否有左值,发现没有,因此它就认为++是一个缺少左值的自增运算符,于是提示提示用户:'++' needs l-value

我们写个程序验证一下上面的推测:

#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<++i+++i+i++<<i<<endl;

cout<<i<<endl;

return 0;

}

果然,编译时有一个错误,提示error C2105: '++' needs l-value ,证实了我们的推测。这个问题的讨论使我们得出一个结论:如果一个变量Ni的两侧都有++或--运算符并且Ni左边的表达式不能分解成X+或X-的形式,那么编译就会出错,X是有值变量。结论有点绕口,举例说明吧:



程序1

#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<i+++i++<<endl;

cout<<i<<endl;

return 0;

}

程序1说明:表达式i+++i++中第二个i的左右两侧都有++,于是我们看第二个i的左侧,左侧是i+++,可以分解为(i++)+,其中“(i++)”是有值变量,符合X+的形式,因此i+++i++是合法表达式,可以通过编译。



程序2

#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<++i+++i<<endl;

cout<<i<<endl;

return 0;

}

程序2说明:表达式++i+++i中第一个i的左右两侧都有++,于是我们看第一个i的左侧,左侧是++,不能分解成X+的形式,因此该表达式不合法。编译时会提示:error C2105: '++' needs l-value



下面,我们再来讨论一下关于i+++i的问题。曾经有人问,表达式i+++i在编译时,编译系统是怎么拆分的?究竟是拆分成(i++)+i呢,还是拆分成i+(++i)。

这个问题本身的答案很简单,是(i++)+i,不明白的自己去看编译原理。这个问题令人感兴趣之处并不在这里,不知道大家注意到i+(++i)这个表达式有什么奇特的地方没有?假设有如下程序:



#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<i+(++i)<<endl;

cout<<i<<endl;

return 0;

}

大家可以猜测一下程序的运行结果。



很多人可能会说是7和4,看起来的确像这样。但是,非常遗憾,实验再一次证明,你可能猜错了,结果是8和4。为什么是8和4呢?前面说过int i=3;cout<< (++i)+(++i) <<endl;的情况,编译系统会先将i连续自加1两次,然后将(++i)一律判定为5进行结算,输出10。这里同理,编译系统现将i自加1,然后再对i+(++i)做运算,(++i)的值判定为4,i的值也判定为4,因此计算结果是8。



下面我们来讨论int i=3;cout<<i++<<” and ”<<i++<<endl;的问题。首先请看如下程序,猜测输出结果:



#include <iostream>

using namespace std;



int main()

{

int i=3;

cout<<i++<<" and "<<i++<<endl;

cout<<i<<endl;

return 0;

}

很多人认为输出结果应该是“3 and 4”和5。我们把代码复制到VC6.0或VC2005上编译运行一下,看看结果……

好了,运行结束,结果是“4 and 3”和5。Oh!My God!Can you tell me why?上帝不会告诉你,我可以告诉你。这是因为很多编译系统在处理输出流时,是从右至左的。在上面的例子中,两处i++处于同一个输出序列中,编译系统会先计算处于右侧的第二个i++,这时i的值为3,因此右侧i++的值为3,之后,i+1变成4,计算第一个i++的值为4,计算完之后将i的值再加1,最后才是输出结果,所以输出结果是4和3。

图片内容