【问题标题】:Why does an object returned by value have the same address as the object inside the method?为什么值返回的对象与方法内部的对象地址相同?
【发布时间】:2020-05-14 08:00:23
【问题描述】:
#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]));
}

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

输出:

$ vim main.cpp 
$ cc -std=c++11 -lc++ main.cpp
$ ./a.out
0x7ffee28d28ac, 0x7ffee28d28f0, 0x7ff401402c00
0x7ffee28d290c, 0x7ffee28d28f0, 0x7ff401402c00
$ 

【问题讨论】:

  • 试试godbolt.org
  • 在堆上(即main之外)声明b时也是这种情况吗?

标签: c++ c++11


【解决方案1】:

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

【讨论】:

  • @willzeng NRVO 可能是可选的,但自 1990 年代初以来已普遍实施。一些编译器确实提供了一种禁用它的方法,对于 gcc 它是“-fno-elide-constructors”
  • @Cubbi 不是实现的问题。如果不是强制性的,那么您不能依赖地址相同。
  • 由于您没有以任何其他方式使用b,编译器可能认为内联调用foo()是合适的
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    g++ -fno-elide-constructors main.cpp
    

    main.cpp 包含您的代码。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-11
      • 2023-03-07
      • 1970-01-01
      • 2021-09-30
      • 1970-01-01
      • 2022-10-24
      • 1970-01-01
      • 2014-11-27
      相关资源
      最近更新 更多