-
x+=x*=x
是否属于未定义行为? - 有人可以解释一下顺序求值中的这条规则吗?什么是“单次求值”?什么是“单次求值”的相反操作?
14) 关于不确定顺序的函数调用,复合赋值运算符以及前缀和后缀递增和递减运算符的操作都是单次求值。
x+=x*=x
是否属于未定义行为?14) 关于不确定顺序的函数调用,复合赋值运算符以及前缀和后缀递增和递减运算符的操作都是单次求值。
x+=x*=x
的行为是未定义的,因为在序列点之间x
被赋值了两次。
C11、C17中对应14)的文本如下:
形式为E1 op= E2的复合赋值等同于简单的赋值表达式E1 = E1 op (E2),除了lvalue E1仅被计算一次,并且在不确定顺序的函数调用方面,复合赋值的操作是单一评估。
我认为它的意思是
int x = 0;
int foo(void) {
x = 5;
return x;
}
int main(void) {
int y = foo() + (x += 2);
}
int main(void) {
int _tmp = x += 2;
int y = foo() + _tmp;
}
或者
int main(void) {
int _tmp = foo();
int y = _tmp + (x += 2);
}
并且不能被分割,例如:
int main(void) {
int _tmp = x;
int _tmp2 = foo();
x = _tmp + 2;
int y = _tmp2 + x;
}
*p += f();
的东西,标准是否明确指出调用是否可以在评估 p
和对该左值的访问之间发生?我认为C和C++标准都将受益于承认作为赋值运算符左操作数使用的左值,或者其地址被取出,必须在分配或第一次使用生成的地址之前“解析”,并明确指定这种解析如何与表达式评估的其他部分排序。 - supercatint _tmp = x+2; int _tmp2 = foo(); x = _tmp;
呢?在这种情况下,x也变成了2。x+=2
的值是按顺序计算的,但它的副作用不是。在C11之后会发生这种情况吗? - imba-tjdp
以获取指针值,只有解引用会在“单个操作”中发生http://port70.net/~nsz/c/c11/n1570.html#note113 - Antti Haapala -- Слава Україні1)是的。
C17 6.5.16 赋值运算符,§3
操作数的评估是无序的。
这使得它由于 C17 6.5 §2 而是未定义的行为
如果对标量对象的副作用相对于同一标量对象上的另一个不同的副作用或使用相同标量对象的值计算是未排序的,则行为未定义。
每个赋值都是一个副作用。C99有相同的规则,但更容易理解:
C99 6.5 §2
在前一个和下一个序列点之间,对象的存储值最多可以通过表达式的求值修改一次。此外,应仅读取先前的值以确定要存储的值。
2) 所有带有C11序列更改的文本阅读起来相当混乱。它们使用一些在任何地方都没有正式定义的术语,例如“单个评估”。
规范文本为C17 6.5.15.2 §3
形式为E1 op = E2的复合赋值等同于简单的赋值表达式E1 = E1 op(E2),除了lvalue E1仅被计算一次,并且在关于不确定顺序的函数调用的情况下,复合赋值的操作是单个评估。
我想这只是意味着像int x;
… x += 1
这样的操作应该产生如下的机器代码:
否则,假设在这些操作之间发生了一些序列,更新了x
。但无论如何,复合赋值都不是原子的,所以我不太理解标准在这里指的是什么。
z = foo() + (x += y)
,foo
要么看到 x
已经被改变,在这种情况下,通过改变全局 x
,foo()
不能影响评估 x += y
的值,要么 foo()
在完全执行之前,x += y
就已经被评估了。 - Antti Haapala -- Слава Україні
x
在序列点之间被修改了两次。为什么还会有任何疑问或争论呢? - Steve Summit