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

Как ссылки хранятся внутри С++?

Мне просто интересно, как ссылки хранятся внутри? Я чувствовал, что понимание в глубине этого уровня позволит мне лучше понять указатель на концепцию и ссылку на решение.

Я подозреваю, что он в основном работает так же, как и указатели, но компилятор заботится о обработке указателей. Просьба сообщить.

4b9b3361

Ответ 1

Ссылки - это просто псевдонимы внутри, компилятор рассматривает их так же, как указатели.

Но для пользователя с точки зрения использования есть несколько тонких различий.

Некоторые из основных отличий:

  • Указатели могут быть NULL, а ссылки cannot.There ничего не называется NULL ссылкой.
  • Ссылка A const продлевает время жизни временной привязки к ней. Нет эквивалента указателям.

Кроме того, ссылки имеют некоторые общие черты с указателями const (а не указатель на const):

  • Ссылки должны быть инициализированы во время создания.
  • Ссылка постоянно привязана к одному месту хранения и не может быть впоследствии восстановлена.

Когда вы знаете, что у вас есть что-то (объект) для ссылки, и вы никогда не захотите ссылаться ни на что другое, используйте указатели ссылки else else.

Ответ 2

Нет никакого требования, чтобы ссылка "хранилась" каким-либо образом вообще. Что касается языка, ссылка является просто псевдонимом какого-либо существующего объекта и что все, что должен предоставить любой компилятор.

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

В ситуациях, когда ссылка должна быть показана (например, при вызове функции в другой единицы перевода), вы можете практически реализовать T & x как T * const и рассматривать каждое вхождение x как неявное разыменование этот указатель. Даже на более высоком уровне вы можете думать о T & x = y; и T * const p = &y; (и соответственно о x и *p) как существенно эквивалентном, поэтому это было бы очевидным способом реализации ссылок.

Но, конечно, нет никаких требований, и любая реализация может делать все, что захочет.

Ответ 3

Извините за использование сборки, чтобы объяснить это, но я думаю, что это лучший способ понять, как ссылки выполняются компиляторами.

#include <iostream>

using namespace std;

int main()
{
    int i = 10;
    int *ptrToI = &i;
    int &refToI = i;

    cout << "i = " << i << "\n";
    cout << "&i = " << &i << "\n";

    cout << "ptrToI = " << ptrToI << "\n";
    cout << "*ptrToI = " << *ptrToI << "\n";
    cout << "&ptrToI = " << &ptrToI << "\n";

    cout << "refToNum = " << refToI << "\n";
    //cout << "*refToNum = " << *refToI << "\n";
    cout << "&refToNum = " << &refToI << "\n";

    return 0;
}

Вывод этого кода похож на этот

i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8

Давайте посмотрим на разборку (я использовал GDB для этого. 8,9 и 10 здесь - номера строк кода)

8           int i = 10;
0x08048698 <main()+18>: movl   $0xa,-0x10(%ebp)

Здесь $0xa - 10 (десятичный), который мы присваиваем i. -0x10(%ebp) здесь означает содержимое ebp register -16 (десятичное). -0x10(%ebp) указывает на адрес i на стеке.

9           int *ptrToI = &i;
0x0804869f <main()+25>: lea    -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov    %eax,-0x14(%ebp)

Назначьте адрес от i до ptrToI. ptrToI снова находится в стеке, расположенном по адресу -0x14(%ebp), то есть ebp - 20 (десятичный).

10          int &refToI = i;
0x080486a5 <main()+31>: lea    -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov    %eax,-0xc(%ebp)

Теперь вот улов! Сравните разборку строк 9 и 10, и вы будете наблюдать, что -0x14(%ebp) заменяется на -0xc(%ebp) в строке 10. -0xc(%ebp) является адресом refToNum. Он выделяется на стеке. Но вы никогда не сможете получить этот адрес от своего кода, потому что вам не обязательно знать адрес.

Итак, ссылка занимает память. В этом случае это стек памяти, так как мы выделили его как локальную переменную. Сколько памяти он занимает? Как много указатель занимает.

Теперь посмотрим, как мы обращаемся к ссылке и указателям. Для простоты я показал только часть фрагмента сборки

16          cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>:        mov    -0x14(%ebp),%eax
0x08048749 <main()+195>:        mov    (%eax),%ebx
19          cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>:        mov    -0xc(%ebp),%eax
0x080487b3 <main()+301>:        mov    (%eax),%ebx

Теперь сравните две вышеперечисленные строки, вы увидите поразительное сходство. -0xc(%ebp) - это фактический адрес refToI, который никогда не доступен для вас. Проще говоря, если вы считаете ссылку ссылкой как обычный указатель, то доступ к ссылке похож на выбор значения по адресу, на который указывает эта ссылка. Это означает, что ниже двух строк кода даст вам тот же результат

cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";

Теперь сравните этот

15          cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>:        mov    -0x14(%ebp),%ebx
21          cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>:        mov    -0xc(%ebp),%eax

Я думаю, вы можете определить, что здесь происходит. Если вы запрашиваете &refToI, возвращается содержимое адреса адреса -0xc(%ebp) и -0xc(%ebp) находится где refToI, а его содержимое - это только адрес i.

Последнее: почему эта строка прокомментирована?

//cout << "*refToNum = " << *refToI << "\n";

Потому что *refToI не разрешен, и он даст вам ошибку времени компиляции.