0

Почему объект, возвращаемый по значению, имеет тот же адрес, что и объект внутри метода?

5

Проблема: Почему адреса переменных a и b совпадают?

Я столкнулся с интересным поведением в моем коде на C++, где две переменные a и b, каждая из которых объявлена в разных контекстах (функции foo и main), имеют одинаковый адрес в памяти. Вот код, который я использовал:

#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]));
}

Вывод программы выглядит следующим образом:

0x7ffee28d28ac, 0x7ffee28d28f0, 0x7ff401402c00
0x7ffee28d290c, 0x7ffee28d28f0, 0x7ff401402c00

Как видно, адрес переменной a (внутри функции foo) и адрес переменной b (в функции main) совпадают. Это поведение кажется странным и вызывает вопросы. Неужели это какая-то оптимизация компилятора, связанная с "кросс-стековыми кадрами"? Любопытно, что результат оказывается одинаковым, даже если я компилирую с флагом -O0.

Есть ли объяснение этому явлению? Как это связано со стековой памятью и жизненным циклом переменных?

3 ответ(ов)

0

Это связано с устранением копий (copy elision) и оптимизацией именованных возвращаемых значений (NRVO). Функция foo возвращает именованный объект a. Поэтому компилятор не создает локальный объект и не возвращает его копию, а создает объект прямо в том месте, где его использует вызывающий код. Вы можете узнать больше об этом на сайте https://en.cppreference.com/w/cpp/language/copy_elision. С C++17 применение RVO является обязательным, тогда как NRVO – нет, но, похоже, ваш компилятор поддерживает это даже с параметром -O0.

0

Обратите внимание, что даже без элиминации копирования (обязательной или нет), уже возможно, что адреса двух объектов будут одинаковыми, поскольку их времена жизни не пересекаются.

0

Как объяснили другие, это происходит из-за оптимизации копирования (copy elision). Вы можете отключить эту оптимизацию с помощью компилятора g++ следующим образом:

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

где main.cpp содержит ваш код.

Теперь переменные a и b будут иметь разные адреса, но a[0] и b[0] будут иметь одинаковый адрес из-за семантики перемещения (move semantics).

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь