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

时间:2014-04-02 16:26:24

标签: c++ c expression undefined-behavior operator-precedence

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

  1. 以下表达式之后a的值是多少?
    假设a的初始值为5.Mention步骤
    • A + =(A ++)+(++ A)
  2. 起初我认为这是未定义的行为,因为a已被多次修改 那么我就读了这个问题并且说提到了步骤所以我可能认为这个问题是正确的。

    所以我的问题是:

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

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

2 个答案:

答案 0 :(得分:13)

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

  

在前一个和下一个序列点之间,一个对象应该具有它   通过表达式的评估,最多修改一次存储值。   此外,先前的值应只读以确定该值   存储。

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

  

表达的评估可能产生副作用。肯定的   执行序列中指定的点称为序列点,全部   先前评估的副作用应完整且无任何一方   后续评估的影响应该发生。

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

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

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

  4.   
  5. 完整声明者的结尾。

  6.   
  7. 完整表达的结束;表达式中的表达式;选择语句的控制表达式(如果   或切换);控制表达 while 执行   声明; for 语句的每个表达式;该   在返回语句中表达。

  8.   
  9. 在库函数返回之前。

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

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

  14.   

答案 1 :(得分:5)

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

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

并在段落 6 中说:

  

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

postfix ++是有问题的,因为我们无法确定更新a的副作用何时会在C ++ 11之前发生,而在C中这同时适用于postfix ++和{{ 1}}操作。关于C ++ 11中prefix ++的未定义行为如何变化,请参阅Assignment operator sequencing in C11 expressions

prefix ++操作有问题,因为:

  

[...] E1 op = E2相当于E1 = E1 op E2,但 E1为   仅评估一次 [...]

因此,在C ++ 11中,以下内容从undefined转为定义:

+=

但这仍未定义:

a = ++a + 1 ;

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

从草案C ++ 11标准部分a = a++ + 1 ; 程序执行 15 说:

  

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