Відповідь залежить від точки зору:
Якщо судити за стандартом C ++, ви не можете отримати нульову посилання, оскільки ви спочатку отримуєте невизначене поведінку. Після першої випадковості невизначеної поведінки стандарт дозволяє щось статися. Отже, якщо ви пишете *(int*)0
, у вас вже є невизначена поведінка такою, якою ви є, з мовної стандартної точки зору, дереферентування нульового вказівника. Решта програми не має значення, як тільки це вираження виконується, ви виходите з гри.
Однак на практиці нульові посилання можна легко створити з нульових покажчиків, і ви не помітите, поки ви фактично не спробуєте отримати доступ до значення, що стоїть за нульовою посиланням. Ваш приклад може бути занадто простим, тому що будь-який хороший оптимізуючий компілятор побачить невизначене поведінку та просто оптимізує все, що від нього залежить (нульова посилання навіть не буде створена, вона буде оптимізована).
Однак, оптимізація проживання залежить від компілятора, щоб довести невизначеність поведінки, чого неможливо зробити. Розглянемо цю просту функцію у файлі converter.cpp
:
int& toReference(int* pointer) {
return *pointer;
}
Коли компілятор бачить цю функцію, він не знає, чи вказівник є нульовим покажчиком чи ні. Таким чином, він просто генерує код, який перетворює будь-який вказівник у відповідну посилання. (Btw: Це noop, оскільки вказівники та посилання є точно таким же звіром у асемблері.) Тепер, якщо у вас є інший файл user.cpp
з кодом
#include "converter.h"
void foo() {
int& nullRef = toReference(nullptr);
cout << nullRef; //crash happens here
}
компілятор не знає, що toReference()
знеструмить переданий покажчик, і припустить, що він повертає дійсну посилання, що на практиці буде нульовою посиланням. Виклик вдається, але коли ви намагаєтесь скористатись посиланням, програма виходить з ладу. Сподіваємось. Стандарт дозволяє щось статися, включаючи появу рожевих слонів.
Ви можете запитати, чому це актуально, зрештою, невизначена поведінка вже спрацьовувала всередині toReference()
. Відповідь налагодження: Нульові посилання можуть поширюватися та поширюватися так само, як це роблять нульові вказівники. Якщо ви не знаєте, що нульові посилання можуть існувати, і навчитесь уникати їх створення, ви можете витратити досить багато часу, намагаючись з’ясувати, чому функція вашого члена, здається, руйнується, коли він просто намагається прочитати звичайний старий int
член (відповідь: екземпляр у виклику члена було нульовою посиланням, так this
само є нульовим вказівником, і ваш член обчислюється як розташований як адреса 8).
То як щодо перевірки наявності нульових посилань? Ви дали рядок
if( & nullReference == 0 ) // null reference
у вашому запитанні. Ну, це не буде працювати: Відповідно до стандарту, у вас є невизначена поведінка, якщо ви відмените нульовий покажчик, і ви не можете створити нульову посилання без відсилання нульового вказівника, тому нульові посилання існують лише всередині сфери невизначеної поведінки. Оскільки ваш компілятор може припустити, що ви не викликаєте не визначену поведінку, він може припустити, що немає такої речі, як нульова посилання (навіть якщо він легко випромінює код, що генерує нульові посилання!). Як такий, він бачить if()
умову, робить висновок, що це не може бути правдою, і просто викинути все if()
твердження. З впровадженням оптимізацій часу посилань стало неможливо надійно перевірити нульові посилання.
TL; DR:
Нульові посилання є дещо жахливим існуванням:
Їх існування здається неможливим (= за стандартом),
але вони існують (= за згенерованим машинним кодом),
але ви не можете їх побачити, якщо вони існують (= ваші спроби будуть оптимізовані),
але вони все одно можуть вбити вас невідомо (= ваша програма виходить з ладу в дивних точках, або ще гірше).
Ваша єдина надія, що вони не існують (= напишіть програму, щоб не створювати їх).
Я сподіваюся, що вас не переслідують!