как функция возвращает в C ++ работает - PullRequest
1 голос
/ 06 марта 2012

Я хочу знать, как компилятор сохранит этот временный int, если я вызову функцию f (3);

int f (int x) { return x; } 

и как это будет оправдано компилятором:

int a=f(3);

это так же, как делать int a = x;(Я знаю, что x будет уже уничтожен) или он действительно создаст временную переменную с именем f (3), например, int f(3)=x;

int& a=f(3);

, и почему это не сработает?

Ответы [ 5 ]

6 голосов
/ 06 марта 2012

Вызовы функций

Компилятор выполнит одно из следующих действий:

  1. Перенесет аргумент в стек и вызовет функцию
    • Для более сложной функциичем у вас, это обычно происходит.
  2. Загрузите аргумент в регистр и вызовите функцию
    • Это может произойти при оптимизации, и тамдостаточно регистров для хранения переменных, которые необходимо передать.
  3. Оптимизация функции полностью (встраивание)
    • Для тривиальной функции, такой как в вашем случае,нормальный компилятор сделает это с самым базовым уровнем оптимизации, так что вы получите ту же сборку, как если бы вы сделали int a = 3.

Справочные переменные в C ++

Ссылочная переменная, объявленная в вашем коде как int &a, - это «другое имя для существующей области памяти».Поэтому объявление int &a нигде не выделяет место для int.Он просто объявляет a для ссылки на уже выделенную область памяти.

Это местоположение может быть существующей переменной int b, так что вы говорите:

int b;
int &a = b;

Здесь, a будет ссылаться на то же содержание, что и b.«Новое имя для существующего объекта» - хорошая идиома.

Можно придумать и сказать int &a = array[5], так что a относится к 6-му элементу int массива * 1047.* или int &a = *(int*)0x12345678 для обозначения определенной области памяти, но я отвлекся.

Ваш код

int &a = 3;

не может работать, поскольку 3 - это временный объект, которыйбудут забыты после выполнения заявления.Чтобы понять проблему более фундаментально, подумайте об этом: Если a относится к уже выделенной ячейке памяти, на что она будет ссылаться после выполнения оператора int &a = 3, и временного объекта * 1058 больше нет*?

Это также распространенная проблема со ссылочными переменными в функциях: возвращение ссылки на локальный объект-функция - неопределенное поведение ... но я снова отвлекся.У вас всегда должен быть «живой, выделенный объект» для a, на который можно сослаться, конец истории.

Чуть более подробно о ссылочных переменных

Что обычно происходит дляОператор типа

int a = 3;

состоит в том, что компилятор генерирует код (упрощенно):

  1. загружает постоянную 3 в регистр
  2. загружает регистр в памятьрасположение, выделенное для

Суть в том, что ни в одном случае не существует долгоживущей ячейки памяти, выделенной для объекта 3, поэтому int &a действительно нельзя сделать, чтобы сослаться на этообъект из-за этого.

«долгоживущая ячейка памяти» означает ячейку, которая будет жить после операции присваивания.Регистр, в котором хранится 3, будет перезаписан и повторно использован, вероятно, сразу после операции присваивания, поэтому он даже теоретически не подходит для цели int &a (на практике int &a можно сделать тольков любом случае ссылается на память , а не регистр).

1 голос
/ 06 марта 2012
  1. Это зависит от соглашения о вызовах. При cdecl (что является общим) функция вернет x в регистр EAX. Затем он будет скопирован в регистр, присвоенный a.

Конечно, оптимизирующий компилятор оптимизирует все это до:

int x = 3;

2. Вы не можете иметь ссылку на что-то, чье время жизни объекта закончилось. Время жизни объекта x заканчивается, когда функция завершается.

0 голосов
/ 06 марта 2012

int& a=f(3); - в этом нет ничего плохого, так как вы создаете ссылку на временную переменную . При выходе из функции данные, на которые ссылается ссылка, были очищены, и теперь они являются зависшей ссылкой.

Вполне вероятно, что компилятор будет просто воспринимать int a = f(3); как int a = 3; в описанной вами ситуации, но вы никогда не можете быть уверены, поскольку в конечном итоге это зависит от конкретного компилятора и от того, как он выполняет свои оптимизации.

0 голосов
/ 06 марта 2012
  1. Когда вы вызываете функцию f (3), адрес инструкции оператора вызова сохраняется в регистре, а указатель вашей инструкции переходит на адрес первого оператора функции f. Также новый стек стека для функции f помещается в стек. Когда вызов функции вернется, для возвращенного значения int из f (3) будет создано временное значение, и это то, что будет присвоено x при выполнении int x=f(3); (поэтому возвращаемое значение из f создается во временном элементе, который затем копируется в х) так что да, временный создается для возвращения. Стек, созданный для f (3), также уничтожается.

  2. int& a=f(3); не работает как ссылка. Ссылка - это псевдоним. Псевдоним для чего-то, что уже существует. f (3) возвращает временную копию, которая будет присвоена переменной. Поскольку стек собирается исчезать после вызова f (3), вы не можете присвоить ему ссылку.

0 голосов
/ 06 марта 2012

Любой разумный компилятор может (и может) превратить

int a=f(3);

в

int a=3;

. Вызов первоначально скомпилируется в нечто вроде отправки адреса возврата в стек, а затем аргумента;сама функция скомпилируется с выталкиванием аргумента, возвращением адреса возврата и повторным нажатием аргумента, а затем переходом к адресу возврата.Тривиальный оптимизатор обнаружил бы, что никакой полезной работы не было сделано, и оптимизировал бы все это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...