定义的参数评估顺序导致次优代码?

众所周知,c和c ++中的参数评估顺序没有定义:例如: foo(a(),b())在上面的调用中,由编译器的实现决定哪个评估顺序为选择哪个function首先执行。 最近我的一位朋友问为什么在C或C ++中没有指定评估顺序。 当我用Google搜索时,我发现指定评估顺序会导致代码生成次优。 但是怎么回事? 为什么定义的参数评估顺序会导致次优代码? 当我提到Java的论证评估顺序时。 我在规范中找到了以下内容。

15.7.4。 参数列表从左到右进行评估

在方法或构造函数调用或类实例创建表达式中,参数表达式可能出现在括号内,以逗号分隔。 每个参数表达式似乎在其右侧的任何参数表达式的任何部分之前被完全计算。 如果参数表达式的求值突然完成,那么右边的任何参数表达式的任何部分都不会被评估?

既然如此,Java有一个定义的参数评估顺序,但是如果指定了这样的行为,那么说C或C ++编译器会产生次优代码似乎有些奇怪。 你能对此有所了解吗?

这部分是历史性的:例如,在具有很少寄存器的处理器上,一种传统(和简单)优化技术是评估首先需要最多寄存器的子表达式。 例如,如果一个子表达式需要5个寄存器,而另一个子表达式需要4个寄存器,则可以将需要5的寄存器的结果保存在需要4的寄存器中。

这可能与通常认为的相关性较低。 如果表达式没有副作用,编译器可以重新排序(即使在Java中),或者重新排序不会改变程序的可观察行为。 现代编译器能够在二十多年前(当制定C ++规则时)比编译器更好地确定这一点。 并且大概是,当他们无法确定这一点时,你在每个表达中都做得足够多,以至于额外的溢出对内存无关紧要。

至少,这是我的直觉。 我被至少有一个人告知我,他们实际上在优化器上工作会产生重大影响,所以我不会说我对它很有把握。

编辑:

只是添加一些关于Java模型的注释。 在设计Java时,它被设计为解释语言。 极端的表现不是问题; 目标是极端安全和可再生性。 因此,它非常精确地指定了许多内容,因此无论平台如何,编译的任何程序都将具有完全相同的行为。 应该没有未定义的行为,没有实现定义的行为,也没有未指定的行为。 无论成本如何(但相信这可以在任何最普遍的机器上以合理的成本完成)。 C(和间接C ++)的一个初始设计目标是,不必要的额外运行时成本应该是最小的,平台之间的一致性不是目标(因为有时甚至普通平台变化很大),而且安全性,而关注点,不是原始的。 虽然态度已经发展了一些,但仍然有一个目标是能够有效地支持任何可能在那里的机器。 无需最新,最复杂的编译器技术。 不同的目标自然会导致不同的解决方案。

Java提出了一个基于堆栈的虚拟机,其中重新排序操作数没有任何优势。 根据James Kanze的回答,C和大多数完全编译的语言都有一个寄存器架构,其中寄存器“溢出”到内存中是昂贵的并且非常值得避免,因此对操作数进行重新排序可能会更好,实际上可以做各种各样的操作。事情,以最大化寄存器使用并最大限度地减少溢出。

我想我们已经过分了。 真正的答案可能是在C标准之前的旧时代,当K&R是事实上的标准时,没有人费心去指定评估参数的顺序,不同的编译器实现以不同的方式进行。

从人的角度来看, 逻辑方法是从左到右评估参数(就像Java那样)。 从编译器的角度来看, 简单的方法是从右到左进行参数评估。 这样,一旦评估了一个参数,它就不需要保存在任何地方,它可以被推入堆栈以备呼叫。 大多数使用堆栈作为参数的C实现需要以相反的顺序推送它们。 这是因为如果没有在同一个源文件中定义函数并且程序员过去利用它来提供原始forms的可变函数,K&R C无法让编译器弄清楚函数需要多少个参数。

因此,标准编写者面临着选择以“正确”的方式(从左到右)进行操作,可能会破坏大量代码,或者按照大多数现有编译器的方式进行操作,并可能破坏其他代码或坚持使用现状并让编译器设计者选择做什么。

无论如何,这是我的意见,不是基于任何事实。

不适用于您的示例的函数求值,但对于简单表达式,这两个表达式甚至可以并行执行。 现代架构是流水线的,并且可以更有效地同时(几乎)同时馈送两个管道,使得必须执行的操作重叠。

此外,您似乎认为只有两个表达式可以为参数进行评估,但有四个: aba()b() 。 函数调用的部分顺序是从左到右

  a -- a() \ f --- f(a(), b()) / b -- b() 

从图中可以看出,存在许多潜在的并行性,现代编译器可以通过没有规定的评估顺序来获得某些东西。

编辑:鉴于讨论的一些其他细节。 如果a()b()是函数调用,则标准保证这些函数调用不会交错,即使函数是内联的。 那么上面的图片应该有一个附加约束, a()b()必须以某种方式排序。 (我不知道如何把它放在图片中。)

另一方面,如果它们是其他表达式,例如宏评估,则如果存在增益,这些表达式可以(并且可能将)交错。