C++的++

代码如上:

结果如下图:

 

 

 

不同编译器 结果可能不同,属于未定义的行为

这个属于未定义行为,不同的编译器会有不同的结果。

 

参考:

What's the value of i++ + i++?
It's undefined. Basically, in C and C++, if you read a variable twice in an expression where you also write it, the result is undefined. Don't do that. Another example is: 
v[i] = i++;

Related example: 
f(v[i],i++);

Here, the result is undefined because the order of evaluation of function arguments are undefined. 
Having the order of evaluation undefined is claimed to yield better performing code. Compilers could warn about such examples, which are typically subtle bugs (or potential subtle bugs). I'm disappointed that after decades, most compilers still don't warn, leaving that job to specialized, separate, and underused tools.
http://www2.research.att.com/~bs/bs_faq2.html

 

 http://hi.baidu.com/wolfand11/blog/item/364217a2623ef3a2caefd0b4.html

 

http://blog.csdn.net/fangyi86/archive/2010/11/14/6008395.aspx

 

http://en.wikipedia.org/wiki/Undefined_behavior

 

---------------------------------------------------------------------------------------------------------------

http://bbs.chinaunix.net/thread-1837276-1-1.html

 

踩踩下面代码的结果:

 #include <iostream>
  using namespace std;
   int main(void)
  {

 int C=0;
 
 if(++C==C++)  
 {
  cout<<"yes"<<endl;
 }
 cout<<C<<endl;
 
 int D;
 C=D=C++;
 cout<<C<<endl;
 cout<<D<<endl;
 
 cout<<C<<"   "<<D<<endl;
 
 
 return 0;
}

 

结果是: yes 2 3 2

 

 

3.        使用“++”的常见错误
到目前为止,我们终于将两种“++”运算符不同的运算含义解释清楚了。然而,在现实的运用中,这个两个“++”运算符还是会经常让我们感到头痛,甚至很多成熟的编程团队会将其定义为尽量避免使用的运算符。那么,这个谜一样的运算符,为什么会成为不少程序员极力回避的禁忌呢?
我们首先来看看下面这个简单的代码片段。
i = 3;
j = ++ i + i;
按照前面我们讲过的原理,在第二行的表达式里,CPU读取第一个i值(求++i的值)之前,需要完成将i赋值为i+1这个副效应。但问题在于,在前后两个序点之间,CPU需要两次读取i的值,我们并不清楚会先读哪个i,这个次序选择权在于编译器。我们根本无法控制。也就是说,上面那句话可能表示两种不同的运算语意,产生两种完全不同的运算结果。
语意一
完成副效应i=i+1(i的值变为4)
读第一个i值(此时赋值的副效应已经完成,i的值为4)
读第二个i值(i值已经变为了4,这个i值自然也不例外)
两次读得的i值相加,把结果写入j内存(结果即是8)
语意二
读第二个i值(此时i值为3)
完成副效应i=i+1(i的值变为4)
读第一个i值(此时赋值的副效应已经完成,i的值为4,于是出现了第一个i值和第二个i值之间的值并不相等的现象)
两次读得的i值相加,把结果写入j内存(结果竟然是7)
由于C语言并未明确规定这些运算的次序,因此在完全符合C语言语法规则的前提下,竟然能得到两种结果,这就是所谓的“二义性”。
编写程序时候是不可能容忍代码存在这种“二义性”的,否则程序很可能就变成了“鸡同鸭讲”。代码必须具备唯一确定的语意。
除了涉及到序点,C语言没有规定编译器在这种情况下应该究竟选择哪种语意,这样,表达式 j = ++ i + i就成了一种未定义行为。如同前面曾经提到的那样,这种未定义行为尽管不违背C语言的语法规则, 但本质上却是一种错误的代码。
以这里讨论的表达式为例,在求“+”运算符右边的i值的时候,从C语言或代码的角度来说,并不能确定i在内存中确切的值。因为在求“+”左面的操作数——表达式“++i”的值的时候可能改变i的值。由于没有规定求“++i”的值和求“+”右边的i值这两个动作之间的次序,于是求表达式“++ i + i”的值就成了一个未定义的行为。
未定义的行为出现在代码中,就是一个“语病”。只不过这里我们说的“语病”不是那种不符合语法要求的语病,而是那种语法上符合要求,但在语言或代码层面却无法确定其唯一含义的语病。比如,有一个大家很熟悉的广告词――“xx皮鞋,足以自豪的皮鞋”,语法上这句话绝对没有问题,但那个“足”字显然是一语双关的,作为广告语这很好,但编程不是做广告,计算机也不会听你忽悠,它只接受具体明确的、不带有“二义性”的指令。而代码中没有语法错误的“二义性”会导致编译器为你“胡乱”选择一种语意。这当然是不可接受的。
根据程序运算结果揣测j = ++ i + i这样未定义行为没有确定含义的表达式的含义是肤浅幼稚的。因为未定义行为不但是不可能预测的,同样也不可以逆向推测。它产生什么样的后果都不奇怪,哪怕让机器死机,关闭电源甚至火山爆发。C语言的学习者之间经常会出现很多类似这样的可笑对话:一个学习者问,为什么这个计算机(编译器)说“足以自豪的皮鞋”里面的“足”字是“脚”的意思,而不是“足够”的意思?另一个学习者立刻反驳,不对!我的计算机(编译器)明明说“足”是足够的意思嘛!
这两个不明就里的学习者也许会争论上好一阵子,却也得不出一个所以然来。本书的读者对此应该有个清晰的认识,能够很轻松地告诉他们代码“二义性”的来龙去脉。
下面,列出了一些C语言中典型的“二义性”例子。
int i = 3,j;
j = (++i)+(++i)+(++i);
(i++)+ (i++)+(i++)
i = i++
printf(“%d %d/n”, i , i++ );
p=(++p>0)?(p++) :p++);
j = (i = 4) + (i = 5) ;
执行 int k = 11 ; k = 1/3*k++;后,k的值是____。
a += a -= a * a
这些例子,都会让编译器陷入那个“足”是脚还是足够的疑惑。写出这种表达式的人,说明其对于运算符的真实含义还是缺乏了解。可惜的是,在现在国内很多专业的C语言论坛中,还是会有不少程序员,在这个问题上疑惑不解。
这些人往往都还有另一个误区,这个误区就是把优先级和结合性与运算次序相混淆,他们难以理解为什么优先级高的反而后计算。比如下面的表达式:
j + i ++
在这个表达式中,“++”的优先级最高,但这个运算却不是最先进行的。这里的优先级只是决定了“++”这个运算符的运算对象是i,而不是“j+i”,即:
j + (i ++ )
也就是说这个表达式的意义是计算“j+ i”的值,再加上一个副效应。而这个副效应发生的时间,我们只知道会是在编译器求完i值之后,但我们无法知道会是发生在计算“j+i”值的之前还是之后。
然而,不少人把优先级理解成了小学里的“先乘除后加减”,这是完全的误解。这里需要再次强调的是,优先级和运算次序完全是两回事!
对于初学者来说,另外一个错误不得不提。就是,++或--(无论前缀或后缀),只能用于左值。比如,int i; “++i”是可以的,因为i是左值;但++(i+1)是一个语法错误。因为(i+1)只有值的含义不可能表示一块连续的具有类型含义的内存(左值),因此(i+1)只是一个右值表达式。在目前这个学习阶段,只有变量名这种初级表达式是左值表达式。
4.        总结
好了,现在我们完全搞清了“++”运算符的来龙去脉。那么,在代码中应该如何避免上面所提到的“二义性”问题呢?
首先,我们需要把握一个原则,即不在两个序点之间更改同一个变量(严格的术语是对象)两次或更多次(a += a -= a * a就是违背了这种原则的错误代码)。如果两个序点之间只写一次同一对象的值,但同时还存在着读这个对象值的情况,那么必须确保写这个对象的值发生在读这个对象值之后。所以,表达式 i = i + 1 的行为是确定的,而表达式++ i + i则属于未定义的行为。
其次,尽量少使用可能引发“二义性”的复杂表达式。熟练的程序员在使用“++”这类运算符时是极其审慎的,在利用“++”的副效应时,一定要确保不会发生出乎自己意料之外的结果。
或许有人会问,这么麻烦干什么,直接取消可恶的“副效应”不久可以了吗?然而,“副效应”真的那么可恶吗?是否取消了副效应就可以一了百了了呢?其实不是的。
副效应不一定是什么坏事。比如前面例子中for语句中的“++”就是利用了其将i值加1的副效应使得代码写得非常简洁,而求得的i值本身倒是没有什么用处的。
而且,没有副效应的表达式语句,在编译器看来是可以不理睬的废话。比如:
2 + 4 ;
这句话,几乎所有的编译器都不会执行。我们最常用的printf()函数,其实多数情况下使用的是它的副效应,而函数调用得到的值几乎很少被用到。编译器对这样有副效应的表达式语句不可能置之不理。
因此,副效应是非常有用的,有时候甚至是必须的。作为一个合格的程序员,应该善于使用副效应。但是在涉及到改变变量在内存中的值的表达式中,一定要慎重,否则就会像前文中那些例子一样,画虎不成反成犬。


 

(++C==C++)    ,这已经违反不在两个序点之间更改同一个变量两次的原则

 

 

 经过你的提醒,我确实对这个问题有了更深的认识:
(1)先说优先级问题:优先级并不定义各运算符所做的运算操作次序,而是定义在涉及需要通过同一对象计算操作数时,哪些操作符需要的操作数优先得到计算。(应该是这样吧)
     C++语言只规定了&&,||和?:操作符的操作数计算次序,其他操作符则没有规定。
(2)先说++C==C++这个判断操作,依据操作符优先级,==这个操作所需的操作数都得让位于前++和后++操作符对其操作数的计算,但是是先通过++C获得操作数还是先通过C++获得操作数,C语言没有规定。这里可能我们大多数的编译器应该都选择了先通过++C操作后获取操作数,亦即是1,所以再等待C++返回1时,两个值刚好相等。
(3)再说C=C++这类赋值操作,=需要获取两个操作数,一个是通过直接读取C,一个是通过C++后获得,但是是先通过读取C值获得还是先通过C++操作好获得,C语言没有定义,如果编译器选择前者,则第一个C值读入,计算C++并返回原值,并对第一个C赋值,最后完成C+1效应,C还是增加了1。
虽然不能理所当然认为每步都是按操作符先后出现进行运算,但我总怀疑是不是优先级高的运算符总是因为需要为前面出现的运算符返回操作数而优先得到执行运算操作?

 

-----------------------------------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值