编译器是否会优化 return 结构具有固定大小数组的函数?

Will the compiler optimize functions which return structures with fixed size arrays?

假设我在 C/C++ 中有一个具有固定大小数组成员的 struct,例如:

#define SIZE 10000
struct foo{
  int vector_i[SIZE];
  float vector_f[SIZE];
};

我想创建一个函数,它将 return foo 的一个实例,例如:

foo func(int value_i, float value_f){
  int i;
  foo f;
  for(i=0;i<SIZE;i++) f.vector_i[i] = value_i;
  for(i=0;i<SIZE;i++) f.vector_f[i] = value_f;
  return f;
}

如果我调用该函数使用:

foo ff = func(1,1.1);

编译器会执行某种优化(即 TCO)吗?

可执行文件会直接填充 ff 变量,还是先填充 funcf 然后将所有值从 f 复制到 ff

如何检查优化是否执行?

我的回答适用于 c++。

Will the compiler perform some kind of optimization (ie TCO)?

TCO 是指 "tail call optimization" 吗?该函数不会在最后进行函数调用(尾调用,如果您愿意的话),因此优化不适用。

编译器 可以 elide 从 return 值复制到临时值,因为命名 return 值优化。临时文件的复制初始化也可以省略。


How can I check if the optimization is performed?

通过读取生成的汇编代码。

如果您看不懂汇编,另一种方法是添加具有副作用的复制和移动构造函数,并观察这些副作用是否发生。但是,修改程序可能会影响编译器是否决定优化(但不需要副作用来防止复制省略)。


如果不想依赖优化,则应通过引用(c 中的指针)将现有对象显式传递给函数,并就地修改它。


复制省略的标准参考 [class.copy] §31 (current standard draft)

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. [...]

该部分描述了在这种情况下满足的条件。引用是从 2016-04-07 的标准文档草稿中生成的。编号可能因标准文档的不同版本而异,规则也略有变化。引用的部分自 c++03 以来一直没有变化,其中的部分是 [class.copy] §15.

这在 Agner Fog's Calling Conventions document 的第 7.1 节传递和 returning 对象中有很好的记录,Table 7. returning 结构的方法,class 和联合对象。

A struct, class or union object can be returned from a function in registers only if it is sufficiently small and not too complex. If the object is too complex or doesn't fit into the appropriate registers then the caller must supply storage space for the object and pass a pointer to this space as a parameter to the function. The pointer can be passed in a register or on the stack. The same pointer is returned by the function. The detailed rules are given in table 7.

换句话说,大型 return 对象直接在调用者提供的缓冲区(在调用者的堆栈上)中构建。

如果 return 对象的身份在编译时未知,则仍然需要额外的副本,例如:

foo func(bool a) {
    foo x, y;
    // fill x and y
    return a ? x : y; // copying is required here
}