Очень часто, у тех кто успешно освоил указатели, при изучении ссылок возникает вопросы: а чем ссылки отличаются от указателей? Только синтаксисом использования и невозможностью сущестования неинициализованной ссылки? Каким образом сделаны ссылки и как они работают?
Пролить свет на этот вопрос поможет следующее, достаточно удачное объяснение:
Ссылка - это переменная, которая указывает на другую переменную, хранящуюся в той же памяти.
Следует обратить внимание, что в этом определении нигде не сказано, что значение ссылки - это адрес, указывающий на значение другой переменной. Здесь сказано лишь что переменная указывает на другую переменную. И это важно.
Переменная - это по-сути, именованная ячейка (либо именованный набор соседних ячеек) в памяти компьютера. Когда работает компилятор, он для текущего контекста создает таблицу используемых переменных. Таблица выглядит примерно так:
|
Имя переменной |
Тип |
Адрес расположения переменной |
1 |
first |
int |
0x50A5B7F2 |
2 |
second |
long |
0x50A5B804 |
Значение переменной компилятор не хранит - все значения переменных существуют только в момент исполнения программы. Компилятор может только сгенерировать команды инициализации и изменения значений переменных. Поэтому в этой таблице нет столбца со значением переменной.
Теперь если добавить переменную-указатель на first, то в таблице переменных появится еще одна запись:
|
Имя переменной |
Тип |
Адрес расположения переменной |
1 |
first |
int |
0x50A5B7F2 |
2 |
second |
long |
0x50A5B804 |
3 |
pointer_first |
int* |
0x50A5BA08 |
То есть, указатель pointer_first - это переменная, у которой есть свой адрес 0x50A5BA08, и по этому адресу (в момент выполнения программы) будет находиться значение, равное 0x50A5B7F2. То есть, указатель указывает не на переменную first, а на значение переменной, которое расположено в памяти по адресу 0x50A5B7F2. Переменная first может даже перестать существовать, и в месте, где лежали ее данные могут быть размещены другие данные. А указатель pointer_first так и будет указывать на ту же самую ячейку (ячейки) памяти с адресом 0x50A5B7F2. И в этом проблема "сырых" указателей, приводящая к обращениям по неправильным адресам.
Чтобы в каком-то виде решить эти проблемы, и заодно упростить синтаксис, были придуманы ссылки. Для объяснения работы ссылок, можно создать ссылку на переменную second. В таблице переменных появится еще одна запись:
|
Имя переменной |
Тип |
Адрес расположения переменной |
1 |
first |
int |
0x50A5B7F2 |
2 |
second |
long |
0x50A5B804 |
3 |
pointer_first |
int* |
0x50A5BA08 |
4 |
link_second |
long& |
0x50A5BA10 |
Чтобы было проще, можно сказать, что переменная-ссылка link_second - это переменная, у которой, как и у любой другой переменной, есть свой адрес 0x50A5BA10, в котором хранится некое значение. Но это значение - не адрес, по которому находятся данные переменной second. Нет! В этом значении хранится ссылка на переменную second. Можно считать, что там хранится номер строки таблицы, в данном случае 2.
Из предыдущего абзаца следует, что невозможно создать ссылку на несуществующую переменную, так как ее просто не будет в таблице переменных. Компилятору очень легко контролировать, на какую переменную ссылается переменная-ссылка. Невозможна ситуация, чтобы переменная-ссылка оказалась неинициализирована: ссылка всегда указывает на какую-то другую переменную (т. е. содержит номер уже существующей переменной).
Вот такого объяснения вполне достаточно для того, чтобы понять отличие ссылки от указателя. Остался только один вопрос: почему при описании внутреннего устройства ссылки использовались такие обтекаемые выражения как "можно сказать", "можно считать, что"? А все дело в том, что ссылки - это, как говорят некоторые программисты - синтаксический сахар, который в конечном итоге компилируется в косвенную адресацию. А как это будет конкретно реализовано - это вопрос к авторам компилятора. Реализация данного механизма может сильно отличаться от компилятора к компилятору. Но так как C++ в некотором смысле высокоуровневый язык, то устройство ссылок можно воспринимать так, как написано выше.