return c * (t /= d) * t * t + b;
所以我期望:
return ((c * (t / d) ^ 3) + b);
但我不确定编译器是否也可以将其解释为:
return ((c * t * t * (t / d)) + b)
我在C标准中搜索过,但没有找到答案。我知道x = x++
是未定义的,但在这里我不确定是因为()
围绕着t /= d
,我认为这迫使编译器首先计算该语句。
我在C标准中搜索,但找不到答案。
你正在寻找的是序列点。
你的表达式:
c * (t /= d) * t * t + b
该语句不包含任何序列点,因此子表达式的求值顺序是任意的。
来自2014-11-19工作草案PDF:N4296的相关文本如下:
1.9程序执行[intro.execution]
...
14与完整表达式相关联的每个值计算和副作用在下一个要评估的完整表达式相关联的每个值计算和副作用之前排序。
15除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的评估是无序的。[注意:在程序执行期间多次评估的表达式中,其子表达式的未排序和不确定排序的评估不需要在不同的评估中一致执行。--结束语] 运算符的操作数的值计算在运算符的结果的值计算之前排序。如果标量对象上的副作用与同一标量对象上的另一个副作用或使用相同标量对象的值计算不是潜在并发的(1.10),则行为未定义。[注意:下一节对可能并发计算施加类似但更复杂的限制。--结束语]
所以在C++中的逻辑是,除非明确地按顺序排列(例如通过使用;
分隔两个完整表达式),否则它们可以以任何顺序发生。
正如第二个高亮部分所提到的那样,当两个未排序的子表达式修改同一对象(或一个修改一个读取)时,行为是未定义的。
return c * (t /= d) * t * t + b;
在C语言(我认为在C++中也是如此)中,这会引发未定义行为。这是因为t
被评估了两次(计算(t /= d)
子表达式),尽管存在一个无序的副作用(由复合赋值运算符产生),该副作用影响到由t
变量表示的对象。
当你遇到UB时,你应该停止考虑表达式的“正确”值。因为任何事情都有可能发生,包括关闭您的PC。
使用-Wall
选项的最新版本的gcc
和clang
可能会告诉您表达式可能会引发UB。在这里,警告如下:
警告:对‘t’的操作可能是未定义的[-Wsequence-point]
警告:未排序的修改和访问‘t’[-Wunsequenced]
(t /= d)
之后,t
的值是多少? - Grzegorz Szpetkowski;
),它 "清除" 了这种情况。更技术性地说,所有副作用都有保证能被完成。 - Grzegorz Szpetkowskit
被读取了两次,而是在没有中间序列点的情况下对t
进行了读写操作。我认为在这里使用“within”也没有意义。 - melpomenereturn a++ + a;
这样的代码是未定义行为,因为在序列点之前不能读取a
两次。 - Grzegorz Szpetkowski
;
在一定程度上会强制编译器。 - Joker_vD