C语言 函数调用顺序[重复]

wr98u20j  于 2023-10-16  发布在  其他
关注(0)|答案(5)|浏览(130)

此问题已在此处有答案

Who defines operator precedence and associativity, and how does it relate to order of evaluation?(6个回答)
What is the order of evaluation for function arguments?(6个回答)
20天前关闭。
在下面的表达式中,函数调用的顺序是什么:

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

它依赖于编译器吗?

wlsrxk51

wlsrxk511#

在C和C++中,每个操作数的求值顺序是unspecified,这意味着,在您的情况下,函数调用的顺序是 unspecified
请注意,它是 unspecifiednotimplementation-defined*。

qjp7pelc

qjp7pelc2#

它在C和C++中都是Unspecified

参考文献:
C++03标准:第5节:表达式,帕拉4:

除非另有说明[例如,&&和的特殊规则||],各个运算符的操作数和各个表达式的子表达式的求值顺序,以及副作用发生的顺序,为未指定

C99标准:第6.5节:

操作符和操作数的分组由语法表示。72)除了后面指定的(对于函数call(),&&,||、?:和逗号运算符),子表达式的求值顺序和副作用发生的顺序都是未指定的

ev7lccsx

ev7lccsx3#

C++:标准保证在到达序列点之前,所有在序列点之前遇到的表达式都被求值。在本例中,=;之间没有序列点,因此顺序未指定。

cqoc49vn

cqoc49vn4#

在这种情况下,顺序无法预测。它不依赖于编译器,它是未指定的;即使使用相同的编译器,你也可以得到不同的求值顺序。

e5nqia27

e5nqia275#

不要只是说这是未指定的,故事结束,让我解释一下如何评估这一点。最重要的是,不要混淆运算符优先级(operator precedence)和运算符求值顺序(order of evaluation)的概念,它们是不同的概念。

  • 在这种情况下,当我们只有基本的数学运算符时,整个表达式本身的求值顺序很容易理解。但是当涉及到其他C操作符时,它就不那么微不足道了。因此,总是通过找出子表达式的求值顺序来开始求值表达式:
  • operator precedence 规则保证对每个编译器都是相同的。它们指出二进制乘法运算符(*)比二进制加法运算符(+)具有更高的优先级。它们都比赋值运算符(=)具有更高的优先级。因此,可以保证子表达式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。这样的代码在一个编译器上可以完美地工作,但在另一个编译器上却会悲惨地崩溃。

相关问题