功能调用顺序

时间:2012-03-22 07:32:29

标签: c++ c

函数在以下表达式中调用的顺序是什么:

a = f1(23, 14) * f2(12/4) + f3();

它取决于编译器吗?

5 个答案:

答案 0 :(得分:12)

在C和C ++中,每个操作数的评估顺序是未指定,这意味着,在您的情况下,函数调用的顺序是未指定,符合标准。< / p>

请注意,未指定 实施定义

答案 1 :(得分:6)

在C和C ++中都是未指定

参考文献:

C ++ 03标准:第5节:表达式,第4段:

  除非另有说明,否则

&amp;&amp; amp;的特殊规则和||],单个运算符的操作数和单个表达式的子表达式的评估顺序,以及发生副作用的顺序,是未指定的

C99标准:第6.5节:

  

运算符和操作数的分组由语法表示.72)除了稍后指定的(对于函数调用(),&amp;&amp;,||,?:和逗号运算符),子表达式的评估顺序和副作用发生的顺序都是未指定的

答案 2 :(得分:4)

C ++:该标准保证在到达该序列点之前评估序列点之前遇到的所有表达式。在您的情况下,=;之间没有序列点,因此订单未指定。

答案 3 :(得分:3)

在这种情况下无法预测订单。它不依赖于编译器,它是未指定的;即使使用相同的编译器,您也可以获得不同的评估顺序。

答案 4 :(得分:0)

让我解释一下这将如何评估,而不仅仅是说这是未指明的,故事的结尾。最重要的是,不要将评估顺序(操作数)的概念与运算符优先级的概念混淆,它们是不同的东西。

  • 在这种情况下,当我们只有基本的数学运算符时,很容易理解整个表达式本身的计算顺序。但是当涉及其他C运营商时,它可能不那么简单。因此,总是通过找出子表达式的评估顺序来开始评估表达式:

  • 运算符优先级规则保证对每个编译器都相同。它们声明二进制乘法运算符(*)的优先级高于二进制加法运算符(+)。它们都比赋值运算符(=)具有更高的优先级。因此,保证首先评估子表达式f1(23, 14) * f2(12/4),然后结果将成为f3()的加法的操作数,最后结果将分配给a

  • 为了说明这一点,表达式等于a = ( (f1(23, 14) * f2(12/4)) + f3() );

  • 所以我们有子表达式f1(23, 14) * f2(12/4)。评估操作数本身的评估顺序未指定的行为,这意味着我们无法知道是先评估f1还是f2操作数。编译器可以从左到右或从右到左自由地评估它们,并且不需要记录适用的方式。我们所知道的是编译器将始终从左到右或从右到左进行评估。

  • 让我们假设特定的编译器从左到右进行评估。然后将首先评估f1(23, 14)。接下来的问题是,将首先评估函数的哪个参数。同样适用于此,函数参数的评估顺序也未指定。在这种情况下,无关紧要,因为两个参数都是整数常量。

  • 按照从左到右的评估顺序,编译器将先评估(并执行)f1,然后再计算f2,然后将它们的结果相乘并将其存储在一个临时的不可见变量中。然后它将评估f3,然后执行添加,最后将结果分配给a

这里学到的重要教训是:由于未指定子表达式的评估顺序,因此每个子表达式不应包含任何取决于评估顺序的副作用。在这个例子中,如果f1和f2都分别将数字1或2写入全局变量,那么如果编译器从左到右进行评估,则该全局变量最后将具有值2,但如果它评估为正确,则该值为1向左。这样的代码在一个编译器上可以完美地工作,但在另一个编译器上会崩溃。