1

在 C 中,操作数的计算顺序与运算符优先级和关联性无关。

假设我在 C: expr1 * expr2 + (expr3 + expr4) (中间没有序列点) 中有一个表达式。

当评估此表达式时:

  1. 由于括号,子表达式 expr3 和 expr4 会在 expr1 和 expr2 之前计算吗?

  2. 或者括号是否确保括号内的运算符在括号外的运算符之前被评估?

括号是否确保操作数或运算符的评估顺序?

4

3 回答 3

6

来自 2011 年语言标准的在线草案:

6.5 表达式
...
3 运算符和操作数的分组由语法指示。85)除非稍后指定,否则子表达式的副作用和值计算是无序的。86)
85) 语法规定了表达式求值中运算符的优先级,与本小节主要小节的顺序相同,最高优先级在前。因此,例如,允许作为二元+运算符 (6.5.6) 的操作数的表达式是 6.5.1 到 6.5.6 中定义的那些表达式。例外情况是作为一元运算符 (6.5.3) 的操作数的强制转换表达式 (6.5.4),以及包含在以下任何一对运算符之间的操作数:分组括号()(6.5.1)、下标括号[](6.5.2.1)、函数调用括号()(6.5.2.2) 和条件运算符? :(6.5.15)。在每个主要子条款中,运算符具有相同的优先级。左或右结合性在每个子条款中由其中讨论的表达式的语法指示。

86) 在程序执行期间多次计算的表达式中,其子表达式的未排序和不确定排序的计算不需要在不同的计算中一致地执行。

像泥一样清澈,对吧?它的意思是,给定这样的表达式

x = a++ + b++ * (--c / ++d)

每个子表达式a++, b++, --c, 和++d可以按任何顺序计算;仅仅因为--cand++d被parens分组并不意味着它们首先被评估。++此外,每个和的副作用--不必在计算表达式后立即应用。

所有运算符优先级保证是 的结果--c / ++d乘以 的结果b++,并将 的结果a++添加到该值;它不保证任何表达式在任何其他表达式之前被评估

密切关注脚注 86;如果上述表达式出现在循环中,则没有理由期望子表达式在每次循环中都以相同的顺序求值。实际上,它们很可能会,但是编译器被明确地赋予了改变事物的自由。

由于这种评估表达式和以任何顺序应用副作用的自由,某些表达式a++ + a++不会给出一致的结果;该标准明确将此称为未定义行为,这意味着编译器没有义务以任何特定方式处理这种情况。它可以忽略这个问题,它可以发出警告,它可以因错误而停止翻译,等等,但它并不要求它做任何特定的事情。

于 2013-07-01T11:26:16.243 回答
2

1. 子表达式 expr3 和 expr4 会在 expr1 和 expr2 之前被评估吗?

不。 C 标准未指定expr1expr2expr3的评估顺序。expr4编译器可以按照它喜欢的任何顺序评估这些子表达式,并且可以根据需要将它们与运算符评估交错。

2.or括号确保括号中的运算符比括号外的运算符先被评估?

是的。括号将覆盖从正常(((expr1 * expr2) + expr3) + expr4)到的运算符优先级((expr1 * expr2) + (expr3 + expr4))。这只是确定了对运算符进行评估的相对顺序。

它可以帮助将评估顺序约束显示为树:

              +
      ________|________
      *               +
  ____|____       ____|____
expr1   expr2   expr3   expr4

此树中节点的评估要求其子节点已被评估。但这是唯一的限制。

评估顺序可以是 : expr2, expr3, expr4, +, expr1, *, +. 或者它可能是: expr4, expr3, +, expr1, expr2, *, +. 或任何其他符合上述限制的排列。

于 2013-07-01T11:11:18.857 回答
1

括号保证它们里面的东西在任何依赖它们的东西之前被评估。不再。

在您的示例中,(expr3+expr4)在将+其添加到expr1*expr2. 这并不意味着它之前被评估过expr1*expr2

于 2013-07-01T11:06:30.613 回答