此代码是否会产生相同的结果?
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
,我认为强制编译器首先计算该语句。
答案 0 :(得分:6)
我在C标准中搜索过但找不到答案
您要搜索的内容是sequence point。
你的表达
c * (t /= d) * t * t + b
不包含任何序列点,因此可以按任何相对顺序评估子表达式。
注意这适用于C,因为您在问题中提到了这一点。您还标记了相关但非常不同的语言C ++,它具有不同的规则。幸运的是,在这种情况下,他们给出了完全相同的结果。
2014-11-19工作草案PDF:N4296的相关文字是
1.9程序执行[intro.execution]
...
14每个值之前,对与完整表达式相关的每个值计算和副作用进行排序 计算和副作用与下一个要评估的完整表达相关联。
15 除非另有说明,否则评估个体经营者的操作数和个体的子表达式 表达式没有排序。 [注意:在执行期间多次计算的表达式中 一个程序,对其子表达式的无序和不确定顺序的评估不一定是 在不同的评估中表现一致。 - 结束注释]的操作数的值计算 在运算符结果的值计算之前对运算符进行排序。如果对标量有副作用 对于相同标量对象的另一个副作用或值计算,对象未被排序 使用相同标量对象的值,它们不是潜在的并发(1.10),行为是 未定义即可。 [注意:下一节对潜在的并发性施加了类似但更复杂的限制 计算。 - 结束说明]
因此,C ++中的逻辑是,除非对事物进行明确排序(例如,通过;
分隔两个完整的表达式),否则它们可以按任何顺序发生。
正如(第二个)突出显示的部分所提到的,当两个未按顺序的子表达式修改同一个对象(或者一个修改并且一个读取)时,行为是未定义的。
答案 1 :(得分:3)
以下声明:
return c * (t /= d) * t * t + b;
在C中调用未定义的行为(我也相信C ++)。这是因为t
被评估两次(计算(t /= d)
子表达式),尽管副作用不明显(由复合赋值运算符产生),这会影响由t
变量表示的对象。 / p>
你遇到UB的那一刻是你应该停止思考的那个"正确的"表达的价值。没有,因为一切皆有可能,包括关闭你的电脑。
最新版本的gcc
和clang
-Wall
可能会告诉您表达式涉嫌调用UB。这里的警告是:
警告:操作' t'可能未定义[-Wsequence-point]
警告:无序修改和访问' [-Wunsequenced]
答案 2 :(得分:3)
上面的表达式,括号使操作顺序显式,如下:
return ((((c * (t /= d)) * t) * t) + b);
然而,这里的问题是此表达式中没有sequence point。因此,可以按任何顺序评估任何子表达式。
例如,编译器可以选择评估t
的值一次,然后使用它出现的每个位置的原始值。相反,它可能会首先评估修改t /= d
的{{1}},然后在其出现的任何其他位置使用此修改后的值。
简而言之,因为您在没有序列点的单个表达式中读取和写入变量,所以您调用undefined behavior。