好的,我知道标准规定C ++实现可以选择评估函数的哪个顺序参数,但是在实际影响它的情况下是否有任何实现“利用”它的实现该计划?
经典示例:
int i = 0;
foo(i++, i++);
注意:我不是在找人告诉我评估的顺序是不可靠的,我很清楚这一点。我只对任何编译器是否真的按照从左到右的顺序进行评估感兴趣,因为我的猜测是如果他们做了很多写得不好的代码就会破坏(这是正确的,但他们仍然可能会抱怨)。 / p>
答案 0 :(得分:51)
它取决于参数类型,被调用函数的调用约定,archtecture和编译器。在x86上,Pascal调用约定从左到右计算参数,而在C调用约定(__cdecl)中,它从右到左。大多数在多个平台上运行的程序都会考虑调用约定以避免意外情况。
如果您有兴趣,可以在Raymond Chen的博客上找到一个很好的article。您可能还想查看GCC手册的Stack and Calling部分。
编辑:只要我们分手:我的回答不是将此视为语言问题,而是将其视为平台问题。语言标准不保证或优先选择其他语言标准并将其保留为未指定。注意措辞。它并没有说这是未定义的。在这个意义上未指定意味着你不能指望的东西,不可移植的行为。我没有C规格/草案方便,但它应该与我的n2798草案(C ++)相似
抽象机器的某些其他方面和操作在本国际标准中被描述为未指定的(例如,对函数的参数的评估顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这些定义了抽象机器的非确定性方面。因此,抽象机器的实例可以为给定程序和给定输入提供多个可能的执行序列。
答案 1 :(得分:7)
我在c++ standards找到了答案。
第5.2.2.8段:
参数的评估顺序未指定。参数表达式求值的所有副作用在输入函数之前生效。后缀表达式和参数表达式列表的评估顺序是 未指定的。
换句话说,它仅取决于编译器。
答案 2 :(得分:6)
这不是您问题的完整副本,但我的回答(以及其他一些内容)也涵盖了您的问题。
有很好的优化原因,为什么编译器可能不只是选择从右到左,而是交错它们。
该标准甚至不保证顺序排序。 仅保证在调用函数时,所有参数都已完全评估。
是的,我已经看到GCC的几个版本就是这样做的。对于你的例子,将调用foo(0,0),之后我将是2。 (我不能给你编译器的确切版本号。就在不久之前 - 但我不会惊讶地看到这种行为再次弹出。这是一种安排指令的有效方法)
答案 3 :(得分:5)
评估所有参数。订单未定义(按标准)。但是C / C ++的所有实现(我都知道)从从右到左评估函数参数。 编辑:CLang是一个例外(见下面的评论)。
我认为从右到左的评估顺序已经非常陈旧(从第一个C编译器开始)。当然,在C ++发明之前,大多数C ++实现都会保持相同的评估顺序,因为早期的C ++实现只是简单地转换为C.
从右到左评估函数参数有一些技术原因。在堆栈体系结构中,参数通常被压入堆栈。在C / C ++中,您可以调用一个参数多于实际指定的函数 - 额外的参数被简单地忽略。如果从左到右评估参数,并从左向右推,那么堆栈指针正下方的堆栈槽将保存最后一个参数,并且函数无法获得任何特定参数的偏移量(因为推送的实际参数数量取决于调用者)。
在从右到左的推送顺序中,堆栈指针正下方的堆栈插槽将始终保存第一个参数,下一个插槽保存第二个参数等。参数偏移将始终是函数的确定性(可能在其他地方编写和编译到一个库中,与它所在的地方分开。
现在,从右到左的推送顺序并不强制从右到左的评估顺序,但在早期的编译器中,内存很少。在从右到左的评估顺序中,可以就地使用相同的堆栈(实际上,在评估参数之后 - 可能是表达式或函数调用! - 返回值已经在正确的位置上堆)。在从左到右的评估中,参数值必须单独存储,并以相反的顺序推回堆栈。
答案 4 :(得分:2)
上次我看到2007年x86硬件上VS2005和GCC 3.x之间存在差异。 所以它(是?)是一种非常可能的情况。所以我再也不会依赖评估订单了。也许现在好多了。
答案 5 :(得分:2)
我希望大多数现代编译器会尝试交错计算参数的指令,因为C ++标准要求它们是独立的,因此缺乏任何相互依赖性。这样做有助于保持CPU的执行单元完全流水线化,从而提高吞吐量。 (至少我希望声称是优化编译器的编译器在给出优化标志时会这样做。)