正如我们所知道的:评估的顺序取决于优先级和相关性。 对于此示例,关联性确定a + b,然后结果加c。这是符合ANSI C标准的编译器所做的(省略优化)。但是它会像标题中的前述方式一样进行评估吗?在什么编译器?在K& R C?
答案 0 :(得分:4)
答案 1 :(得分:2)
只要最终结果相同,编译器就可以自由重新排列。
例如:
1 + b + 1
可以轻松转换为:
b + 2
答案 2 :(得分:1)
用数学术语表示的等式结构(在a+(b*c)
中我们讨论b*c
被评估为“第一”)并不一定与编译器评估参数的顺序有关
此实例中的实际执行顺序是未定义的IIRC。 C只保证由序列点分隔的表达式的顺序保持不变,+运算符不是序列点。
大多数编译器都会按预期执行 - 生成代码然后评估a b然后c
答案 3 :(得分:1)
6.5表达
...
3运算符和操作数的分组由语法指示。 74)除非另有说明 稍后(对于函数调用()
,&&
,||
,?:
和逗号运算符),评估顺序 子表达式和发生副作用的顺序都是未指明的。
...
74)语法指定运算符在表达式求值中的优先级,它是相同的 作为本条款主要子条款的顺序,首先是最高优先级。因此,例如, 表达式允许二进制+
运算符(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)。
在每个主要子条款中,运算符具有相同的优先级。左或右相关性是 在每个子条款中通过其中讨论的表达式的语法表示。
强调我的。表达式a + b + c
将被评估为(a + b) + c
;也就是说,c
的结果将添加到a + b
的结果中。在评估a
之前,必须先评估b
和a + b
,但可以按任意顺序评估a
,b
和c
。
答案 4 :(得分:0)
答案 5 :(得分:0)
我会尝试强调您认为评估顺序与编译器认为的差异。
在数学上我们说在表达式a + b * c
中,在加法之前计算乘法。当然必须是因为我们需要知道要添加到a
的内容。
但是,编译器在评估b * c
之前不一定要考虑评估表达式a
。您可能认为因为乘法具有更高的优先级,所以编译器将首先查看表达式的该部分。实际上,无法保证编译器将首先决定做什么。它可以先评估a
,或b
或c
。标准未指定这种行为。
为了演示,我们来看看以下代码:
#include <iostream>
int f() { std::cout << "f\n"; return 1; }
int g() { std::cout << "g\n"; return 2; }
int h() { std::cout << "h\n"; return 3; }
int main(int argc, const char* argv[])
{
int x = f() + g() * h();
std::cout << x << std::endl;
return 0;
}
每个函数f()
,g()
和h()
只是将函数的名称输出到标准输出,然后分别返回1,2或3。
当程序启动时,我们将变量x
初始化为f() + g() * h()
。这正是我们之前看到的表达方式。答案当然是7.现在,天真地你可以假设乘法首先发生,所以它会去那里它会g()
,然后乘以h()
,然后它会执行f()
并将其添加到上一个结果中。
实际上,使用GCC 4.4.5进行编译会告诉我,函数按照它们在表达式中出现的顺序执行:f()
,然后是g()
,然后是h()
。这并不是所有编译器都必须发生的事情。这完全取决于编译器如何做到这一点。
如果您正在执行关联或可交换的操作,那么编译器也可以自由地交换表达式中的数学分组,但仅当结果正好>他们是一样的。编译器必须小心,不要进行任何可能导致溢出发生的重组,而这些重组都不会发生。只要结果是标准定义的,编译器就可以自由地做它想做的事情。