求值时的未定义行为

本文探讨了C++编程中的未定义行为,特别是涉及到求值顺序和副作用的问题。例如,`i = i++ + i++`等表达式可能导致未定义的行为,因为编译器并未规定计算子表达式的顺序。文章引用了n3225标准文档,强调了在相同对象上产生副作用的顺序未指定,这使得某些操作成为未定义行为。此外,还通过示例解释了如何避免这种情况,并提及Haskell中的Monad作为对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

int i = 1;
int a = i++ + i++;

这是一个很多初学都喜欢问题,是某些人喜欢考的问题,是某些人喜欢自以为是地回答的问题。
见上一篇文章,所谓的结合性和优先级是怎么回事,而结合性和优先级在这里没有决定性的影响。

一般的命令式的语言,都是有一些可操作的对象,通过一些操作,改变这些对象的状态,最后
达到计算的目的。换句话说,是靠副作用进行的计算。里面一般会有两种语法结构:
表达式,语句。
而表达式后面加上;会成为表达式语句。
表达式会有值,会有类型,求值过程就是计算的过程。
语句的话,纯粹是进行某个操作,没有值,没有类型。(一些实现可以对这个进行扩展)

换句话说,语句就是为了产生副作用。
而表达式可能有副作用!!!

比如:
a = i++;
有两个副作用,一个是改变a的值,另一个是改变i的值。
如果忽略掉对a的值的改变,那么副作用只有一个了。

上面讲的是副作用,现在看看求值顺序:
int a = b + c;
   +
 b   c

在计算右边的表达式的值的时候,首先要知道b和c的值,但是b和c究竟哪个先知道,这是没有指定的。
但是b,c在+之前是可以肯定的,这是一个天然的依赖关系(2003标准中似乎是没有提,n3225中有提)。

求值顺序和副作用产生的顺序有什么关系?这里不提,规避之。

n3225中如下说到:
在一个数量对象上的副作用,如果和另一个在相同对象上的副作用,或者使用了这个对象的值的计算,
之间是没有指定顺序的话,那么行为是未定义的。

看几个例子(引自n3225):
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined

[=的两个子表达式中,右边的++对i有副作用,=对i的赋值有副作用,虽然在求值上要先分别对i和v[i++]求值
但是对副作用而言,是不清楚哪个在哪个前的,如果++在前,结果就是v[i],否则是=在前,结果就是v[i]+1]。

i = 7, i++, i++; // i becomes 9
[行为确定,左边的副作用在右边之前产生]

i = i++ + 1; // the behavior is undefined
[两个副作用的顺序的问题]

i = i + 1; // the value of i is incremented
[只有一个副作用]

f(i = -1, i = -1); // the behavior is undefined
[看上去,从结果上讲,这个式子的结果是完全可以定义的,但是满足了未定义行为的定义,所以是未定义行为]

}

PS:
只是求值的顺序未确定也会有一些东东:
int f1(){printf("hello ");return 0;}
int f2(){printf("world! ");return 0;}
int a = f1()+f2();
先f1还是先f2是没有指定的。
如果认为这种输出是一个副作用,那么这里求值的未指定导致了副作用顺序的未指定。

int gx = 5;
int f1(){return gx++;}
int f2(){return gx++;}
double y = 1.0*f1()/f2();
只看对y的定义,只知道f1(),f2()求值顺序未指定,再看里面,可以知道求值顺序的未指定导致了副作用的未指定。


注1:在ISO/IEC 14882(2003)中的陈述中指出这样的行为未定义,但是在例子中使用的是未指定行为。
在n3225中,在陈述中使用的是未定义行为,在例子中也用的是未定义行为。
注2:在n3225已经丢弃了“序列点”的概念了,在2003版的标准中也有很多不清楚的,写得很纠结。

2011/01/19 23:30
创建
2013/06/29

读者可以参考haskell中的monad

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值