在C / C ++的每本教科书中,您都会找到一个运算符优先级和关联表,如下所示:
http://en.cppreference.com/w/cpp/language/operator_precedence
StackOverflow上的一个问题是这样的:
以下功能的执行顺序为:
f1() * f2() + f3();
f1() + f2() * f3();
参考上面的图表,我自信地回答说,函数具有从左到右的关联性,因此在前面的语句中,在这两种情况下都会像这样评估:
f1() - > f2() - > F3()
评估功能后,您完成评估,如下所示:
(a1 * a2)+ a3
a1 +(a2 * a3)
令我惊讶的是,很多人告诉我,我错了。决心证明他们错了,我决定转向ANSI C11标准。我再一次惊讶地发现在运算符优先级和关联性上提到的很少。
答案 0 :(得分:43)
运算符优先级在相应的标准中定义。 C和C ++的标准是C和C ++究竟是什么的唯一真实定义。所以,如果你仔细观察,细节就在那里。实际上,详细信息在语言的语法中。例如,看一下C ++中+
和-
的语法生成规则(统称为 additive-expressions ):
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
如您所见, multiplicative-expression 是 additive-expression 的子规则。这意味着,如果您有x + y * z
之类的内容,则y * z
表达式是x + y * z
的子表达式。这定义了这两个运算符之间的优先级。
我们还可以看到 additive-expression 的左操作数扩展为另一个 additive-expression ,这意味着x + y + z
,{{1它是它的子表达式。这定义了关联性。
关联性确定如何对相同运算符的相邻使用进行分组。例如,x + y
是从左到右关联的,这意味着+
将按如下方式进行分组:x + y + z
。
请勿将其误认为是评估顺序。绝对没有理由在(x + y) + z
之前无法计算z
的值。重要的是计算x + y
而不是x + y
。
对于函数调用运算符,从左到右的关联性意味着y + z
(如果f()()
返回一个函数指针,可能会发生这种情况)被分组如下:f
(当然,另一个方向没有任何意义)。
现在让我们考虑你正在看的例子:
(f())()
f1() + f2() * f3()
运算符的优先级高于*
运算符,因此表达式的分组如下:
+
我们甚至不必在此考虑关联性,因为我们没有任何相同的运算符彼此相邻。
对函数调用表达式的评估完全没有排序。没有理由不首先调用f1() + (f2() * f3())
,然后调用f3
,然后调用f1
。在这种情况下,唯一的要求是在运算符之前评估运算符的操作数。这意味着在评估f2
之前必须调用f2
和f3
,并且必须评估*
并且必须在*
之前调用f1
评估{1}}。
+
中,x || y
始终在x
之前进行评估。这允许短路,如果已知y
已为y
,则不需要评估x
。
评估顺序先前在C和C ++中使用序列点进行了定义,并且两者都改变了术语以根据之前的关系来定义事物。有关详细信息,请参阅Undefined Behaviour and Sequence Points。
答案 1 :(得分:10)
C标准中运算符的优先级由语法表示。
(C99,6.5p3)“运算符和操作数的分组由语法表示.74)”
74)“语法指定运算符在评估表达式”
时的优先级
C99理由也说
“优先规则被编码为每个运算符的语法规则。”
和
“关联性规则同样被编码到句法规则中。”
另请注意,关联性与评估顺序无关。在:
f1()* f2()+ f3()
以任何顺序评估函数调用。 C语法规则表明f1() * f2() + f3()
表示(f1() * f2()) + f3()
,但表达式中操作数的评估顺序未指定。
答案 2 :(得分:8)
考虑优先级和关联性的一种方法是假设语言只允许包含赋值和一个运算符的语句,而不是多个运算符。所以声明如下:
a = f1() * f2() + f3();
是不允许的,因为它有5个运算符:3个函数调用,乘法和加法。在这种简化的语言中,您必须将所有内容分配给临时对象,然后将它们组合起来:
temp1 = f1();
temp2 = f2();
temp3 = temp1 * temp2;
temp4 = f3();
a = temp3 + temp4;
关联性和优先级指定必须以该顺序执行最后两个语句,因为乘法具有比加法更高的优先级。但它没有指定前3个陈述的相对顺序;它会同样有效:
temp4 = f3();
temp2 = f2();
temp1 = f1();
temp3 = temp1 * temp2;
a = temp3 + temp4;
sftrabbit给出了一个示例,其中函数调用运算符的关联性是相关的:
a = f()();
如上所述简化时,会变为:
temp = f();
a = temp();
答案 3 :(得分:4)
标准中定义了优先级和关联性,它们决定了如何构建语法树。优先级按运算符类型工作(1+2*3
为1+(2*3)
而不是(1+2)*3
),关联性按操作员位置工作(1+2+3
为(1+2)+3
而不是1+(2+3)
)。
评估顺序不同 - 它没有定义如何构建语法树 - 它定义了如何evaluate
语法树中运算符的节点。评估顺序定义为不定义 - 您永远不能依赖它,因为编译器可以自由选择他们认为合适的任何顺序。这样做是为了使编译器可以尝试优化代码。这个想法是程序员编写的代码不应该受到评估顺序的影响,并且无论顺序如何都会产生相同的结果。
答案 4 :(得分:3)
从左到右的关联性意味着f() - g() - h()
意味着(f() - g()) - h()
,仅此而已。假设f
返回1
。假设g
返回2
。假设h
返回3
。从左到右的关联性意味着结果为(1 - 2) - 3
或-4
:仍允许编译器首先调用g
和h
,这与关联性无关,但不允许给出1 - (2 - 3)
的结果,这将是完全不同的结果。