当我遇到这个问题时,我正在翻阅我的教科书:
下面的表达式后面的<code>a</code>的值是多少?假设a
=5的初始值。提及步骤。
a+=(a++)+(++a)
起初,我认为这是未定义的行为,因为a
已被多次修改。然后我读了这个问题,上面写着“提及步骤”,所以我可能认为这个问题是正确的。
不,应用括号并不能使其成为定义的行为。它仍然未定义。C99标准§6.5¶2说
在前一个序列点和下一个序列点之间,对象的存储值最多应通过表达式的求值修改一次。此外,前一个值应只读以确定要存储的值。
将子表达式放在括号中可能会强制子表达式的求值顺序,但它不会创建序列点。因此,它不能保证子表达式的副作用(如果它们产生任何副作用)何时发生。再次引用C99标准§5.1.2.3¶2
评估表达式可能会产生副作用。在执行序列中的某些特定点(称为序列点),先前评估的所有副作用都应是完整的,并且不会发生后续评估的副作用。
为了完整起见,以下是附录C中C99标准规定的序列点。
> < li>
对参数求值后对函数的调用。
以下运算符的第一个操作数的结尾:逻辑and
完整声明符的结尾。
一个完整表达式的结束;表达式语句中的表达式;选择语句的控制表达式(if或switch);while或do语句的控制表达式;for语句的每个表达式;return语句中的表达式。
紧接在库函数返回之前。
在与每个格式化的输入/输出函数转换说明符相关联的操作之后。
在每次调用比较函数之前和之后,以及在对比较函数的任何调用和作为参数传递给该调用的对象的任何移动之间。
添加括号不会产生序列点,在更现代的标准中,它不会产生副作用前的序列关系,这是这个表达式的问题,除非特别说明,其余的都是关于C 11的。括号是第< code>5.1节“主要表达式”中包含的主要表达式,其语法如下(重点是我的):
primary-expression:
literal
this
( expression )
[...]
第6段写道:
带括号的表达式是其类型和值与封闭表达式相同的主表达式。带括号的表达式不影响表达式是否为左值。带括号的表达式可以在与封闭表达式完全相同的上下文中使用,并且具有相同的含义,除非另有说明。
后缀
是有问题的,因为我们无法确定更新 a
的副作用何时发生在 C 11 之前,而在 C 中,这适用于后缀
和前缀
操作。关于 C 11 中前缀
的未定义行为如何更改,请参阅 C11 表达式中的赋值运算符排序。
< code> =操作有问题,因为:
〔…〕E1 op=E2等同于E1=E1 op E2,除了E1仅被评估一次〔…〕
所以在C 11中,下面的内容从未定义变成了已定义:
a = ++a + 1 ;
但这仍然没有定义:
a = a++ + 1 ;
以上两者都在 C 11 之前以及 C99 和 C11 中都是未定义的。
从草案C 11标准部分1.9
程序执行第15段中说:
除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是不排序的。[注:在程序执行过程中多次求值的表达式中,其子表达式的未排序和不确定排序求值不需要在不同的求值中一致执行。-结束注释]运算符的操作数的值计算在运算符结果的值计算之前排序。如果相对于相同标量对象上的另一个副作用或使用相同标量对象的值进行的值计算,标量对象的副作用未排序,则行为未定义。