括号是否强制执行评估顺序并定义未定义的表达式?

当我遇到这个问题时,我正在阅读我的教科书

  1. 在下面的表达式之后,a的值是多少?
    假设a的初始值为5.Mention步骤
    • 一个+ =(A ++)+(++ A)

起初我认为这是未定义的行为,因为a已被多次修改。
那么我就读了这个问题并说它提到了步骤所以我可能认为这个问题是正确的。

所以我的问题是:

  • 应用括号是否定义了未定义的行为?
  • 是否在评估括号表达式后创建了序列点?
  • 如果已定义,则括号如何重要,因为++和()具有相同的优先级

注意:一个解释清楚且明确的答案将得到我的投票

不,应用括号不会使其成为已定义的行为。 它仍未定义。 C99标准§6.5¶2说

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。 此外,先前的值应该是只读的,以确定要存储的值。

在括号中放置子表达式可能会强制子表达式的求值顺序,但它不会创建序列点。 因此,它不能保证子表达式的副作用何时会产生任何副作用。 再次引用C99标准§5.1.2.3¶2

表达的评估可能产生副作用。 在称为序列点的执行序列中的某些特定点处,先前评估的所有副作用应是完整的,并且不会发生后续评估的副作用。

为了完整起见,以下是附件C中C99标准规定的序列点。

  1. 在评估参数之后调用函数。

  2. 以下运算符的第一个操作数的结尾:logical AND && ; 逻辑OR || ; 条件 ; 逗号

  3. 完整声明者的结尾。

  4. 完整表达的结束; 表达式中的表达式; 选择语句的控制表达式( ifswitch ); whiledo语句的控制表达式; for语句的每个表达式; 返回语句中的表达式。

  5. 紧接库函数返回之前。

  6. 在与每个格式化的输入/输出函数转换说明符相关联的操作之后。

  7. 紧接在每次调用比较函数之前和之后,以及对比较函数的任何调用和作为参数传递给该调用的对象的任何移动之间。

添加括号不会创建序列点,并且在更现代的标准中,它不会在关系副作用之前创建序列,这是您所拥有的表达式的问题,除非注意到其余部分将与C +相关+11。 括号是第5.1节主要表达式中涵盖的主要表达式 ,它具有以下语法( 强调我的未来 ):

 primary-expression: literal this ( expression ) [...] 

并在第6段中说:

带括号的表达式是一个主表达式,其类型和值与所包含表达式的类型和值相同。 括号的存在不会影响表达式是否为左值。 除非另有说明,否则带括号的表达式可以在可以使用封闭表达式的上下文完全相同的上下文中使用,并且具有相同的含义

postfix ++是有问题的,因为我们无法确定何时更新a的副作用将在C ++ 11之前发生,而在C中这适用于postfix ++prefix ++操作。 关于C ++ 11中prefix ++未定义行为如何变化,请参阅C11表达式中的赋值运算符排序 。

+=操作有问题,因为:

[…] E1 op = E2相当于E1 = E1 op E2,但E1只评估一次 […]

所以在C ++ 11中,以下内容从undefined变为已定义:

 a = ++a + 1 ; 

但这仍未定义:

 a = a++ + 1 ; 

以上两者都是在C ++ 11和C99和C11之间未定义的。

从草案C ++ 11标准第1.9程序执行15段说:

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的。 [注意:在程序执行期间不止一次评估的表达式中,不需要在不同的评估中一致地执行对其子表达式的未序列和不确定顺序的评估。 -end note]运算符操作数的值计算在运算符结果的值计算之前排序。 如果对标量对象的副作用相对于对同一标量对象的另一个副作用或使用相同标量对象的值进行的值计算未进行排序,则行为未定义。