功能参数评估顺序

Avi*_*ash 19 c c++

C/C++,是否有一个固定的顺序来评估函数的参数?我的意思是,标准说什么?难道left-to-right还是right-to-left?我从书中得到了令人困惑的信息.

是否有必要function call使用stack only.什么是C/C++标准,说这个?

Joh*_*ode 33

C和C++是两种完全不同的语言; 不要假设同样的规则总是适用于两者.但是,在参数评估顺序的情况下:

C99:

6.5.2.2函数调用
...
10函数指示符的评估顺序,实际参数和实际参数中的子表达式是未指定的,但在实际调用之前有一个序列点.

[ 编辑 ] C11(草稿):

6.5.2.2函数调用
...
10在评估函数指示符和实际参数之后但在实际调用之前有一个序列点.调用函数(包括其他函数调用)中的每个评估(在执行被调用函数的主体之前或之后没有特别排序)对于被调用函数的执行是不确定地排序的.94)
...
94)换句话说,函数执行不会相互"交错".

C++:

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

两种标准都没有要求使用硬件堆栈来传递函数参数; 这是一个实现细节.C++标准使用术语"展开堆栈"来描述从try块到throw-expression的路径上自动创建的对象的调用析构函数,但就是这样.最流行的架构确实通过硬件堆栈传递参数,但它不是通用的.

[ 编辑 ]

我从书中得到了令人困惑的信息.

这并不是最令人惊讶的,因为很容易就90%写的关于C的书只是废话.

虽然语言标准不是学习 C或C++的好资源,但对于像这样的问题很方便.官方™标准文件需要花钱,但有一些草稿可以在网上免费获得,并且应该足以满足大多数用途.

最新的C99草案(自原始出版物以来有更新)可在此处获得.最新的出版前C11草案(去年正式批准)可在此处获取.此处提供了公开可用的C++语言草案,尽管它有一个明确的免责声明,其中一些信息不完整或不正确.

  • _since很容易写出90%的书籍_...您是否也知道95%的使用统计数据来陈述其真相的陈述是在现场弥补的,除了描述其他统计数据之外完全没用?(顺便说一下很好的答案) (6认同)
  • @thoron:没有.有很多合法的C程序不是合法的C++程序,并且有合法的C程序也是合法的C++程序,但有不同的语义.这两种语言有很多共同点,但有些区域存在很大差异. (3认同)

Luc*_*ore 8

保持安全:标准将其留给编译器来确定参数的计算顺序.因此,您不应该依赖特定的订单.

  • 第二段完全错了.即使调用约定是按从右到左的顺序推送参数,指令重新排序也可以改变评估的顺序. (3认同)

Lun*_*din 5

在 C/C++ 中,函数参数的求值有固定的顺序。我的意思是标准所说的是从左到右还是从右到左。我从书中得到的信息令人困惑。

不,函数参数(以及任何表达式中的两个子表达式)的求值顺序在 C 和 C++ 中是未指定的行为。用简单的英语来说,这意味着可以首先评估最左边的参数,也可以是最右边的参数,并且您无法知道哪个顺序适用于特定编译器

例子:

static int x = 0;

int* func (int val)
{
  x = val;
  return &x;
}

void print (int val1, int val2)
{
  cout << val1 << " " << val2 << endl;
}

print(*func(1), *func(2));
Run Code Online (Sandbox Code Playgroud)

这段代码非常糟糕。它依赖于打印参数的评估顺序。它将打印“1 1”(从右到左)或“2 2”(从左到右),但我们不知道是哪个。标准唯一保证的是对 func() 的两次调用都在对 print() 的调用之前完成。

解决这个问题的方法是注意顺序是未指定的,并编写不依赖于计算顺序的程序。例如:

int val1 = *func(1);
int val2 = *func(2);
print(val1, val2); // Will always print "1 2" on any compiler.
Run Code Online (Sandbox Code Playgroud)

函数调用是否必须仅使用堆栈来实现?C/C++ 标准对此有何规定?

这称为“调用约定”,标准中根本没有指定任何内容。如何传递参数(和返回值)完全取决于实现。它们可以在 CPU 寄存器或堆栈上或以其他方式传递。调用者可以负责将参数压入/弹出堆栈,也可以由函数负责。

函数参数的求值顺序仅与调用约定有一定关系,因为求值发生在调用函数之前。但另一方面,某些编译器可以选择将最右边的参数放入 CPU 寄存器中,并将其余参数放入堆栈中,例如。