C ++中编译器和评估的参数顺序

问题描述 投票:56回答:6

好吧,我知道标准规定C ++实现可以选择评估函数的哪个顺序参数,但是在实际影响程序的情况下是否有任何实现“利用”它的实现?

经典示例:

int i = 0;
foo(i++, i++);

注意:我不是在找人告诉我评估的顺序不能依赖,我很清楚这一点。我只对任何编译器是否真的按照从左到右的顺序进行评估感兴趣,因为我的猜测是,如果他们做了很多写得不好的代码就会破坏(这是正确的,但他们仍然可能会抱怨)。

c++ operator-precedence
6个回答
51
投票

它取决于参数类型,被调用函数的调用约定,archtecture和编译器。在x86上,Pascal调用约定从左到右计算参数,而在C调用约定(__cdecl)中,它从右到左。大多数在多个平台上运行的程序都会考虑调用约定以避免意外情况。

如果您有兴趣,可以在Raymond Chen的博客上找到一个很好的article。您可能还想查看GCC手册的Stack and Calling部分。

编辑:只要我们分裂:我的回答不是将其视为语言问题,而是将其作为平台问题。语言标准不保证或更喜欢一种语言标准,并将其保留为未指定。注意措辞。它并没有说这是未定义的。在这个意义上未指定意味着你不能指望的东西,不可移植的行为。我没有C规格/草案方便但它应该类似于我的n2798草案(C ++)

抽象机器的某些其他方面和操作在本国际标准中被描述为未指定的(例如,对函数的参数的评估顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这些定义了抽象机器的非确定性方面。因此,抽象机器的实例可以具有给定程序和给定输入的多于一个可能的执行序列。


7
投票

我在c++ standards找到了答案。

第5.2.2.8段:

参数的评估顺序未指定。参数表达式求值的所有副作用在输入函数之前生效。未指定后缀表达式和参数表达式列表的评估顺序。

换句话说,它仅取决于编译器。


6
投票

Read this

这不是您问题的精确副本,但我的答案(以及其他一些)也涵盖了您的问题。

有很好的优化原因,为什么编译器可能不只是选择从右到左,而是交错它们。

该标准甚至不保证顺序排序。它只保证在调用函数时,所有参数都已经完全评估。

是的,我已经看到GCC的几个版本就是这样做的。对于你的例子,将调用foo(0,0),之后我将是2。 (我不能给你编译器的确切版本号。它是不久前的 - 但我不会惊讶地看到这种行为再次弹出。这是一种安排指令的有效方法)


5
投票

评估所有参数。订单未定义(按标准)。但是C / C ++的所有实现(我都知道)从右到左评估函数参数。编辑:CLang是一个例外(见下面的评论)。

我相信从右到左的评估顺序已经非常陈旧(自第一个C编译器以来)。当然,在C ++发明之前,大多数C ++实现都会保持相同的评估顺序,因为早期的C ++实现只是简单地转换为C语言。

从右到左评估函数参数有一些技术原因。在堆栈体系结构中,参数通常被压入堆栈。在C / C ++中,您可以调用一个参数多于实际指定的函数 - 额外的参数被简单地忽略。如果从左到右评估参数,并从左向右推,那么堆栈指针正下方的堆栈槽将保存最后一个参数,并且函数无法获得任何特定参数的偏移量(因为推送的实际参数数量取决于调用者)。

在从右到左的推送顺序中,堆栈指针正下方的堆栈槽将始终保持第一个参数,下一个槽保存第二个参数等。参数偏移将始终是函数的确定性(可以写入和在其他地方编译成一个库,与它被调用的地方分开)。

现在,从右到左的推送顺序并不强制要求从右到左的评估顺序,但在早期的编译器中,内存很少。在从右到左的评估顺序中,可以就地使用相同的堆栈(实质上,在评估参数之后 - 可能是表达式或函数调用! - 返回值已经在正确的位置上堆)。在从左到右的评估中,参数值必须单独存储,并以相反的顺序推回堆栈。


2
投票

上次我在2007年的x86硬件上看到了VS2005和GCC 3.x之间的差异。所以这是(很可能?)的情况。所以我再也不会依赖评估订单了。也许现在好多了。


2
投票

我希望大多数现代编译器会尝试交错计算参数的指令,因为C ++标准要求它们是独立的,因此缺乏任何相互依赖性。这样做有助于保持CPU的执行单元完全流水线化,从而提高吞吐量。 (至少我希望声称是优化编译器的编译器在给出优化标志时会这样做。)

© www.soinside.com 2019 - 2024. All rights reserved.