Подтвердить что ты не робот

Возвращаемые указатели на С++/Ссылки

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

Я, однако, запутываюсь, когда вижу такие вещи, как это:

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

int* returnB() {
    return &b;
}

int& returnC() {
    return c;
}

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

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

Может ли кто-нибудь подтвердить, насколько я прав, со всеми четырьмя вопросами?

4b9b3361

Ответ 1

В 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*, который сам по себе является указателем.

Ответ 2

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

  • *, когда используется как часть типа указывает, что тип является указателем: 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.

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

Ответ 3

Тайлер, это было очень полезное объяснение, я сделал некоторый эксперимент, используя визуальный отладчик студии, чтобы еще раз прояснить эту разницу: -

int sample = 90;
int& alias = sample;
int* pointerToSample  = &sample;

Name                  Address                        Type
&alias                0x0112fc1c {90}                int *
&sample               0x0112fc1c {90}                int *
pointerToSample       0x0112fc1c {90}                int *
*pointerToSample    90                       int
alias   90                                       int &
&pointerToSample      0x0112fc04 {0x0112fc1c {90}}   int * *

Макет памяти

PointerToSample       Sample/alias
_______________......____________________
0x0112fc1c |          |   90   |
___________|___.....__|________|_______...

[0x0112fc04]  ...      [0x0112fc1c

Ответ 4

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

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

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

Ответ 5

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

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

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, поведение undefined произойдет при доступе к *j.

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

Будучи педантичным, возвращаемые указатели должны быть объявлены как указывающие на постоянные данные. Возвращенные ссылки должны быть 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) и вызывать поведение undefined.

Ответ 6

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

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