Указатели/Ссылки Возврата C++

У меня есть довольно хорошее понимание оператора разыменования, адрес оператора и указатели в целом.

Я однако запутываюсь, когда я вижу материал, такой как это:

int* returnA() {
    int *j = &a;
    return j;
}

int* returnB() {
    return &b;
}

int& returnC() {
    return c;
}

int& returnC2() {
    int *d = &c;
    return *d;
}
  1. В returnA() Я прошу возвращать указатель; просто для уточнения это работает потому что j указатель?
  2. В returnB() Я прошу возвращать указатель; так как указатель указывает на адрес, причина почему returnB() работы состоят в том, потому что я возвращаюсь &b?
  3. В returnC() Я прошу адрес int быть возвращенным. Когда я возвращаюсь c & оператор автоматически "добавляется" c?
  4. В returnC2() Я прошу снова адрес int быть возвращенным. Делает *d работайте, потому что указатели указывают на адрес?

Предположите, что a, b, c инициализируются как целые числа как Глобальные.

Кто-то может проверить, если я корректен со всеми четырьмя из моих вопросов?

48
задан Mustafa Ekici 21 October 2013 в 06:27
поделиться

5 ответов

В returnA() я прошу вернуть указатель; просто чтобы уточнить, это работает, потому что j - указатель?

Да, int *j = &a инициализирует j, чтобы указать на a. Затем вы возвращаете значение j, то есть адрес a.

В returnB() я прошу вернуть указатель; поскольку указатель указывает на адрес, причина, по которой returnB() работает, заключается в том, что я возвращаю &b?

Да. Здесь происходит то же самое, что и выше, только за один шаг. &b дает адрес b.

В returnC() я прошу вернуть адрес int. Когда я возвращаю c, автоматически добавляется оператор &?

Нет, возвращается ссылка на int. Ссылка не является адресом так же, как указатель - это просто альтернативное имя для переменной. Поэтому вам не нужно применять оператор &, чтобы получить ссылку на переменную.

В returnC2() я снова прошу вернуть адрес int. Работает ли *d, потому что указатели указывают на адрес?

Опять же, возвращается ссылка на int. *d ссылается на исходную переменную c (какой бы она ни была), на которую указывает c. И это может быть неявно превращено в ссылку, как в returnC.

Указатели в общем случае не указывают на адрес (хотя и могут - например, int** является указателем на указатель на int). Указатели являются адресом чего-то. Когда вы объявляете указатель как something*, это something является тем, на что указывает ваш указатель. Поэтому в моем примере выше int** объявляет указатель на int*, который сам является указателем.

29
ответ дан 26 November 2019 в 18:36
поделиться

Хотя Питер ответил на ваш вопрос, вас явно сбивают с толку символы * и & . Сложность в том, что они оба имеют два разных значения, связанных с косвенным обращением (даже исключая третье значение * для умножения и & для побитового - а также).

  • * , когда используется как часть типа указывает, что тип является указателем: int - это тип, поэтому int * является типом указателя на int , а int ** является типом указателя на указатель на int .

  • & при использовании как часть типа указывает, что тип является ссылкой. int - это тип, поэтому int & - это ссылка на int (такой вещи как ссылка на ссылку не существует). Ссылки и указатели используются для похожих вещей, но они совершенно разные и не взаимозаменяемы.Ссылку лучше всего рассматривать как псевдоним или альтернативное имя для существующей переменной. Если x - это int , то вы можете просто назначить int & y = x , чтобы создать новое имя y для x . Послесловия, x и y могут использоваться взаимозаменяемо для обозначения одного и того же целого числа. Два основных следствия этого заключаются в том, что ссылки не могут быть NULL (поскольку должна быть исходная переменная для ссылки) и что вам не нужно использовать какой-либо специальный оператор для получения исходного значения (потому что это просто альтернативное имя, не указатель). Ссылки также нельзя переназначить.

  • * при использовании в качестве унарного оператора выполняет операцию, называемую разыменование (которая не имеет ничего общего со ссылочными типами !). Эта операция имеет смысл только для указателей. Когда вы разыменовываете указатель, вы возвращаете то, на что он указывает. Итак, если p является указателем на int, * p - это int , на который указывает.

  • & при использовании в качестве унарного оператора выполняет операцию, называемую адрес-из . Это довольно понятно; если x - переменная, то & x - это адрес x . Адрес переменной может быть назначен указателю на тип этой переменной.Итак, если x является int , тогда & x может быть назначено указателю типа int * , и этот указатель будет указывать на x . Например. если вы назначите int * p = & x , то * p можно использовать для получения значения x .

Итак, помните, что суффикс типа & предназначен для ссылок и не имеет ничего общего с унарными операциями и , которые имеют отношение к получению адресов для использования с указателями. Эти два использования совершенно не связаны. И * как суффикс типа объявляет указатель, а * как унарный оператор выполняет действие над указателями.

74
ответ дан 26 November 2019 в 18:36
поделиться

В returnC() и returnC2() вы не просите вернуть адрес.

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

Все вы знаете, что ссылка указывает на конкретный объект.
Хотя сама ссылка не является объектом, а лишь альтернативным именем.

3
ответ дан 26 November 2019 в 18:36
поделиться

Все ваши примеры приводят к неопределенному поведению во время выполнения. Вы возвращаете указатели или ссылки на элементы, которые исчезают после выхода из функции.

Позвольте мне пояснить:

int * returnA()
{
  static int a;  // The static keyword keeps the variable from disappearing. 
  int * j = 0;   // Declare a pointer to an int and initialize to location 0.
  j = &a;        // j now points to a.
  return j;      // return the location of the static variable (evil).
}

В вашей функции переменная j назначается для указания на временное местоположение a. После выхода из вашей функции переменная a исчезает, но ее прежнее местоположение возвращается через j. Поскольку a больше не существует в месте, на которое указывает j, при обращении к *j возникнет неопределенное поведение.

Переменные внутри функций не должны быть изменены через ссылку или указатель другим кодом. Это может произойти, хотя и приводит к неопределенному поведению.

Будучи педантичным, возвращаемые указатели должны быть объявлены как указывающие на константные данные. Возвращаемые ссылки должны быть const:

const char * Hello()
{
  static const char text[] = "Hello";
  return text;
}

Приведенная выше функция возвращает указатель на константные данные. Другой код может получить доступ (прочитать) к статическим данным, но не может их модифицировать.

const unsigned int& Counter()
{
  static unsigned int value = 0;
  value = value + 1;
  return value;
}

В приведенной выше функции значение value инициализируется нулем при первом входе. Все последующие выполнения этой функции приводят к увеличению value на единицу. Функция возвращает ссылку на постоянное значение. Это означает, что другие функции могут использовать это значение (издалека), как если бы это была переменная (без необходимости разыменовывать указатель).

В моем понимании указатель используется для необязательного параметра или объекта. Ссылка передается, когда объект должен существовать. Внутри функции параметр со ссылкой означает, что значение существует, однако указатель должен быть проверен на null перед разыменованием. Кроме того, при использовании ссылки существует больше гарантий того, что целевой объект является действительным. Указатель может указывать на недопустимый адрес (не null) и вызвать неопределенное поведение.

2
ответ дан 26 November 2019 в 18:36
поделиться

Семантически, ссылки действуют как адреса. Однако синтаксически это работа компилятора, а не ваша, и вы можете обращаться со ссылкой так, как будто она является исходным объектом, на который она указывает, включая привязку других ссылок к ней, чтобы они тоже ссылались на исходный объект. В этом случае попрощайтесь с арифметикой указателей.

Обратной стороной этого является то, что вы не можете изменить то, на что они ссылаются - они привязаны во время конструирования.

1
ответ дан 26 November 2019 в 18:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: