关于C/C++副作用与顺序点的问题

 

 

在学习c++primer plus过程中遇到了这个问题,查询大量资料后,我总结一下。首先看下面这个问题:

 

int x=1,y;
y = x++ * (5 + x++);
printf("x = %d,y = %d",x,y);

或者以C++写的:

int x=1,y;
y = x++ * (5 + x++);
cout<<x<<endl<<y<<endl;

根据以往做题的经验,你知道x++应先用值再递增,++x应先递增再用值。所以应该这么算y = 1 * (5 + 2) = 7,输出x = 3,y = 7。可是经过编译运行后,结果却是x = 3,y = 6。
再来看几个:

int x1,x2,x3,x4;
x1 =x2 =x3 =x4= 2;
int y1,y2,y3,y4;
y1 = x1++ * (5-x1++);//2*(5-3)=4;
y2 = x2++ * (5-++x2);//2*(5-4)=2;
y3 = ++x3 * (5-x3++);//3*(5-3)=6;
y4 = ++x4 * (5-++x4);//3*(5-4)=3

cout<<y1<<" "<<y2<<" "<<y3<<" "<<y4<<endl

想象中的答案应该是4,2,6,3,然而,控制台输出结果为6,6,6,4。
有人也许碰到过这个问题:

int x = 1;
x = 2 * x++ * (3 - ++x);
cout<<x1<<endl;

此时控制台输出结果为3,但是无论你怎么算也算不到是三,因为对此C++没有定义正确的行为。(以上结果在用c/c++写输出结果都是一致的。)出现这种情况的就是C/C++语句中顺序点和副作用的原因。

副作用:在计算表达式时对某些东西(如变量里的值)进行了修改。
顺序点:程序执行过程中的一个点。 --《c++ primer plus》

C/C++规定,在执行过程中,每当执行到一个顺序点,则该顺序点之前的副作用都应该执行完成,即编译器保证在执行到每一个顺序点时,完成前面的副作用评估,然而在连个顺序点之间到底按什么顺序执行并不能保证。
这就解释了之前这个问题:

x1 = 2;
y1 = x1++ * (5-x1++);//2*(5-3)=4;
cout<<y1<<endl;

一个分号就是一个完整的句子,因此该分号就是一个顺序点。而x1++跟(5 - x1++)并不是完整的句子,因此不能 成为顺序点。因此,这三句有三个顺序点。有三个副作用,两个x1递减和一个赋值给y1.在第二个顺序点时,x1的值完成了两个副作用,可以通过下面的cout证明。但是y1的值却并没有得到想象中的结果,编译器没有根据的你顺序去执行。他只保证了分号结束时x1的值会增加2,但具体以什么样的顺序执行却没有保证。
另外标准还规定,两个相邻顺序点之间,对某一表达式求值,最多只能造成任一特定对象的值被更改一次。如果表达式求值过程会更改某对象的值,那么要求更改前的值被读取的唯一目的,只能是用来确定要存入的新值。
但是实际来看,有很多情况都违反了这个规定。例如上面的x1的值就被更改了两次。具体原因未解。
因此在实际运用中,我们要避免这样的语句。因为他的结果往往是不确定的。其实,实际运用中很少会用到类似y1 = x1++ * (5-x1++);这样的语句吧,除非你在准备考试。。
补:c标准定义顺序点在:
1. 每个完整表达式结束时。完整表达式包括变量初始化表达式,表达式语句,return语句的表达式,以及条件、循环和switch语句的控制表达式(for头部有三个控制表达式);
2. 运算符 &&、||、?: 和逗号运算符的第一个运算对象计算之后;
3. 函数调用中对所有实际参数和函数名表达式(需要调用的函数也可能通过表达式描述)的求值完成之后(进入函数体之前)。
假设时刻ti和ti+1是前后相继的两个顺序点,到了ti+1,任何C/C++ 系统(VC、BC等都是C/C++系统)都必须实现ti之后发生的所有副作用。当然它们也可以不等到时刻ti+1,完全可以选择在时段 [t, ti+1] 之间的任何时刻实现在此期间出现的副作用,因为C/C++ 语言允许这些选择。
参考文章:http://blog.csdn.net/gexueyuan/article/details/9770063点击打开链接

   http://www.tuicool.com/articles/fUBvma点击打开链接

  http://www.cnblogs.com/smwikipedia/articles/1229984.html点击打开链接

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值