假设我在C中有一个表达式:expr1 * expr2 + (expr3 + expr4)
(中间没有序列点)。
评估此表达式时:
将在expr1和expr之前计算子表达式expr3和expr4 expr2因为括号?
- 醇>
或者,括号是否确保括号内的运算符在括号外的运算符之前进行求值?
括号是否确保操作数或运算符的评估顺序?
答案 0 :(得分:5)
来自2011年语言标准的在线草稿:
6.5表达
...
3运算符和操作数的分组由语法指示。 85)除非另有说明 后来,子表达的副作用和价值计算都没有排序。 86)
85)语法指定运算符在表达式求值中的优先级,该子表达式与本子条款的主要子条款的顺序相同,优先级最高。因此,例如,允许作为二进制+
运算符(6.5.6)的操作数的表达式是在 6.5.1至6.5.6。例外情况是强制转换表达式(6.5.4)作为一元运算符的操作数 (6.5.3),以及以下任何一对运算符之间包含的操作数:分组 括号()
(6.5.1),下标括号[]
(6.5.2.1),函数调用括号()
(6.5.2.2),以及 条件运算符? :
(6.5.15)。 在每个主要子条款中,运算符具有相同的优先级。左或右相关性是 在每个子条款中通过其中讨论的表达式的语法表示。
86)在程序执行期间被多次评估的表达式中,未经过排序和 对其子表达式的不确定顺序评估不需要一致地执行 不同的评估。
清楚如泥,对吗?这意味着,给出一个像
这样的表达式x = a++ + b++ * (--c / ++d)
每个子表达式a++
,b++
,--c
和++d
都可以按任意顺序进行评估;仅仅因为--c
和++d
按parens分组并不意味着它们首先被评估。此外,在评估表达式后,不必立即应用每个++
和--
的副作用。
所有运算符优先保证是--c / ++d
的结果将乘以b++
的结果,a++
的结果将被添加到a++ + a++
那个价值;它不保证任何表达式评估之前的任何表达式。
密切注意脚注86;如果上面的表达式出现在循环中,则没有理由期望每次循环都会以相同的顺序评估子表达式。实际上,它们很可能是,但编译器明确地给予了动摇的自由。
由于可以自由地评估表达式并以任何顺序应用副作用,因此某些表达式(如{{1}})不会产生一致的结果;标准明确地将其称为未定义行为,这意味着编译器没有义务以任何特定方式处理该情况。它可以忽略这个问题,它可以发出警告,它可以通过错误停止翻译等,但是没有要求它做任何特定的事情。
答案 1 :(得分:2)
1.子表达式expr3和expr4是否会在expr1和expr2 becoz之前进行评估?
没有。 C标准未指定expr1
,expr2
,expr3
和expr4
的评估顺序。编译器可以按照它喜欢的任何顺序评估这些子表达式,并且可以根据需要将它们与运算符评估交织。
2.或者paranthesis确保在paranthesis之外的操作员将比在paranthesis之外的操作员进行评估?
是。括号将覆盖从正常(((expr1 * expr2) + expr3) + expr4)
到((expr1 * expr2) + (expr3 + expr4))
的运算符优先级。这只是确定运算符的相对顺序。
它可以帮助将评估顺序约束显示为树:
+
________|________
* +
____|____ ____|____
expr1 expr2 expr3 expr4
对此树中节点的评估要求已对其子节点进行了评估。但这是唯一的限制。
评估顺序可以是:expr2
,expr3
,expr4
,+
,expr1
,*
,+
。
或者可以是:expr4
,expr3
,+
,expr1
,expr2
,*
,+
。
或任何符合上述限制的其他排列。
答案 2 :(得分:1)
括号保证在它们内部的内容在依赖于它们的任何内容之前进行评估。没有了。
在您的示例中,(expr3+expr4)
会在将+
添加到expr1*expr2
之前进行评估。这并不意味着它在expr1*expr2
之前进行了评估。