为什么值返回的对象和方法内部的对象有相同的地址?

wil*_*eng 56 c++ c++11

#include <stdio.h>
#include <array>
#include <vector>

std::vector<int> foo() {
 int i;
 std::vector<int> a(100);
 printf("%p, %p, %p\n", &i, &a, &(a[0]));
 return a;
}

int main() {
 int i;
 std::vector<int> b = foo();
 printf("%p, %p, %p\n", &i, &b, &(b[0]));
}
Run Code Online (Sandbox Code Playgroud)

为什么a和上面b有相同的地址?这是某种“跨堆栈框架”优化吗?即使我使用该-O0选项,结果也是一样的。

输出:

$ vim main.cpp 
$ cc -std=c++11 -lc++ main.cpp
$ ./a.out
0x7ffee28d28ac, 0x7ffee28d28f0, 0x7ff401402c00
0x7ffee28d290c, 0x7ffee28d28f0, 0x7ff401402c00
$ 
Run Code Online (Sandbox Code Playgroud)

Wer*_*nze 57

这是因为复制省略/命名返回值优化 (NRVO)。foo返回一个命名对象a。所以编译器不是创建本地对象并返回它的副本,而是在调用者放置它的地方创建对象。您可以在https://en.cppreference.com/w/cpp/language/copy_elision上阅读更多相关信息。虽然自 C++17 起 RVO 是强制性的,但 NRVO 不是,但看起来您的编译器即使在-O0.

  • @Cubbi这不是是否实施的问题。如果不是强制性的,那么您就不能*依赖*地址是相同的。 (5认同)
  • @willzeng NRVO 可能是可选的,但自 20 世纪 90 年代初以来已普遍实施。一些编译器确实提供了一种禁用它的方法,对于 gcc 来说它是“-fno-elide-constructors” (4认同)

R..*_*R.. 15

请注意,即使没有复制省略(强制性或非强制性),两个对象的地址也可能相同,因为它们的生命周期是不重叠的。


Hik*_*hat 8

正如其他人所解释的,这是因为复制 elison。您可以通过以下方式使用 g++ 禁用它:

g++ -fno-elide-constructors main.cpp
Run Code Online (Sandbox Code Playgroud)

其中main.cpp包含您的代码。

现在ab有不同的地址,但由于移动语义a[0]b[0]将有相同的地址。