C++ 中的编译器和参数求值顺序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/621542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:19:20  来源:igfitidea点击:

Compilers and argument order of evaluation in C++

c++operator-precedence

提问by RaptorFactor

Okay, I'm aware that the standard dictates that a C++ implementation may choose in which order arguments of a function are evaluated, but are there any implementations that actually 'take advantage' of this in a scenario where it would actually affect the program?

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

Classic Example:

经典示例:

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

Note: I'm not looking for someone to tell me that the order of evaluation can't be relied on, I'm well aware of that. I'm only interested in whether any compilers actually do evaluate out of a left-to-right order because my guess would be that if they did lots of poorly written code would break (rightly so, but they would still probably complain).

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

采纳答案by dirkgently

It depends on the argument type, the called function's calling convention, the archtecture and the compiler. On an x86, the Pascalcalling convention evaluates arguments left to right whereas in the C calling convention (__cdecl) it is right to left. Most programs which run on multiple platforms do take into account the calling conventions to skip surprises.

它取决于参数类型、被调用函数的调用约定、架构和编译器。在 x86 上,Pascal调用约定从左到右计算参数,而在 C 调用约定 ( __cdecl) 中,它是从右到左计算的。大多数在多个平台上运行的程序确实考虑了调用约定以跳过意外。

There is a nice articleon Raymond Chen' blog if you are interested. You may also want to take a look at the Stack and Callingsection of the GCC manual.

如果您有兴趣,可以在 Raymond Chen 的博客上找到一篇不错的文章。您可能还想查看GCC 手册的堆栈和调用部分。

Edit:So long as we are splitting hairs: My answer treats this not as a language question but as a platform one. The language standard does not gurantee or prefer one over the other and leaves it as unspecified. Note the wording. It does not say this is undefined. Unspecified in this sense means something you cannot count on, non-portable behavior. I don't have the C spec/draft handy but it should be similar to that from my n2798 draft (C++)

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

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine. An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input.

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

回答by Igor

I found answer in c++ standards.

我在c++ 标准中找到了答案。

Paragraph 5.2.2.8:

第 5.2.2.8 段:

The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.

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

In other words, It depends on compiler only.

换句话说,它只依赖于编译器。

回答by jalf

Read this

读这个

It's not an exact copy of your question, but my answer (and a few others) cover your question as well.

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

There are very good optimization reasons why the compiler might not just choose right-to-left but also interleave them.

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

The standard doesn't even guarantee a sequential ordering. It onlyguarantees that when the function gets called, all arguments have been fully evaluated.

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

And yes, I have seen a few versions of GCC do exactly this. For your example, foo(0,0) would be called, and i would be 2 afterwards. (I can't give you the exact version number of the compiler. It was a while ago - but I wouldn't be surprised to see this behavior pop up again. It's an efficient way to schedule instructions)

是的,我已经看到一些版本的 GCC 正是这样做的。对于您的示例,将调用 foo(0,0) ,之后 i 将是 2 。(我无法为您提供编译器的确切版本号。那是前一阵子了 - 但看到这种行为再次出现我不会感到惊讶。这是安排指令的有效方式)

回答by Stephen Chung

All arguments are evaluated. Order not defined (as per standard). But all implementations of C/C++ (that I know of) evaluate function arguments from right to left. EDIT: CLang is an exception (see comment below).

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

I believe that the right-to-left evaluation order has been very very old (since the first C compilers). Certainly way before C++ was invented, and most implementations of C++ would be keeping the same evaluation order because early C++ implementations simply translated into C.

我相信从右到左的评估顺序已经非常古老了(从第一个 C 编译器开始)。当然,在 C++ 发明之前,大多数 C++ 实现将保持相同的评估顺序,因为早期的 C++ 实现只是简单地转换为 C。

There are some technical reasons for evaluating function arguments right-to-left. In stack architectures, arguments are typically pushed onto the stack. In C/C++, you can call a function with more arguments than actually specified -- the extra arguments are simiply ignored. If arguments are evaluated left-to-right, and pushed left-to-right, then the stack slot right under the stack pointer will hold the last argument, and there is no way for the function to get at the offset of any particular argument (because the actual number of arguments pushed depends on the caller).

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

In a right-to-left push order, the stack slot right under the stack pointer will always hold the first argument, and the next slot holds the second argument etc. Argument offsets will always be deterministic for the function (which may be written and compiled elsewhere into a library, separately from where it is called).

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

Now, right-to-left push order does not mandate right-to-left evaluation order, but in early compilers, memory is scarce. In right-to-left evaluation order, the same stack can be used in-place (essentially, after evaluating the argument -- which may be an expression or a funciton call! -- the return value is already at the right position on the stack). In left-to-right evaluation, the argument values must be stored separately and the pushed back to the stack in reverse order.

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

回答by Robert Gould

Last time I saw differences was between VS2005 and GCC 3.x on an x86 hardware in 2007. So it's (was?) a very likely situation. So I never rely on evaluation order anymore. Maybe it's better now.

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

回答by j_random_hacker

I expect that most modern compilers would attempt to interleave the instructions computing the arguments, given that they are required by the C++ standard to be independent and thus lack any interdependencies. Doing this should help to keep a deeply-pipelined CPU's execution units full and thereby increase throughput. (At least I would expect that a compiler that claims to be an optimising compiler would do so when optimisation flags are given.)

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