Действителен ли этот фрагмент кода (и определено ли поведение)?
int &nullReference = *(int*)0;
Оба г ++ и лязг ++ компиляция без какого - либо предупреждения, даже при использовании -Wall
, -Wextra
, -std=c++98
, -pedantic
, -Weffc++
...
Конечно, ссылка на самом деле не является нулевой, поскольку к ней нельзя получить доступ (это означало бы разыменование нулевого указателя), но мы могли бы проверить, является ли он нулевым, проверив его адрес:
if( & nullReference == 0 ) // null reference
c++
reference
null
language-lawyer
Пеоро
источник
источник
if (false)
, исключая проверку, именно потому, что ссылки в любом случае не могут быть нулевыми. Лучшая документированная версия существовала в ядре Linux, где была оптимизирована очень похожая проверка NULL: isc.sans.edu/diary.html?storyid=6820Ответы:
Ссылки не являются указателями.
8.3.2 / 1:
1.9 / 4:
Как говорит Йоханнес в удаленном ответе, есть некоторые сомнения в том, что «разыменование нулевого указателя» должно быть категорически объявлено неопределенным поведением. Но это не один из случаев, вызывающих сомнения, поскольку нулевой указатель определенно не указывает на «действительный объект или функцию», и в комитете по стандартам нет желания вводить пустые ссылки.
источник
&*p
какp
универсальное, это не исключает неопределенного поведения (которое по своей природе может «показаться работающим»); и я не согласен с тем, чтоtypeid
выражение, которое пытается определить тип «разыменованного нулевого указателя», фактически разыменовывает нулевой указатель. Я видел, как люди серьезно спорят о том, что на них&a[size_of_array]
нельзя и не следует полагаться, и в любом случае проще и безопаснее просто писатьa + size_of_array
.*p
ссылается lvalue , когдаp
это нулевой указатель. В C ++ в настоящее время нет понятия пустого lvalue, которое хотел представить проблема 232.typeid
работе на основе синтаксиса, а не на основе семантики. То есть, если уtypeid(0, *(ostream*)0)
вас действительно есть неопределенное поведение - неbad_typeid
гарантируется, что будет выбрано no , даже если вы семантически передаете lvalue, возникающее в результате разыменования нулевого указателя. Но синтаксически на верхнем уровне это не разыменование, а выражение оператора запятой.Ответ зависит от вашей точки зрения:
Если вы судите по стандарту C ++, вы не можете получить нулевую ссылку, потому что сначала вы получаете неопределенное поведение. После этого первого случая неопределенного поведения стандарт позволяет случиться чему угодно. Итак, если вы пишете
*(int*)0
, у вас уже есть неопределенное поведение, поскольку вы, с точки зрения стандарта языка, разыменовываете нулевой указатель. Остальная часть программы не имеет значения, как только это выражение выполнено, вы выбываете из игры.Однако на практике пустые ссылки можно легко создать из нулевых указателей, и вы этого не заметите, пока не попытаетесь получить доступ к значению, стоящему за нулевой ссылкой. Ваш пример может быть слишком простым, поскольку любой хороший оптимизирующий компилятор увидит неопределенное поведение и просто оптимизирует все, что от него зависит (нулевая ссылка даже не будет создана, она будет оптимизирована).
Тем не менее, эта оптимизация зависит от компилятора, чтобы доказать неопределенное поведение, что может быть невозможно. Рассмотрим эту простую функцию внутри файла
converter.cpp
:Когда компилятор видит эту функцию, он не знает, является ли указатель нулевым указателем или нет. Таким образом, он просто генерирует код, который превращает любой указатель в соответствующую ссылку. (Кстати: это пустяк, поскольку указатели и ссылки - это одно и то же в ассемблере.) Теперь, если у вас есть другой файл
user.cpp
с кодомкомпилятор не знает, что
toReference()
разыменует переданный указатель, и предполагает, что он возвращает действительную ссылку, которая на практике оказывается пустой ссылкой. Вызов завершается успешно, но при попытке использовать ссылку программа вылетает. С надеждой. Стандарт позволяет случиться чему угодно, в том числе появлению розовых слонов.Вы можете спросить, почему это актуально, ведь внутри уже сработало неопределенное поведение
toReference()
. Ответ - отладка: пустые ссылки могут распространяться и размножаться так же, как и нулевые указатели. Если вы не знаете, что могут существовать пустые ссылки, и научитесь избегать их создания, вы можете потратить немало времени, пытаясь выяснить, почему ваша функция-член дает сбой, когда она просто пытается прочитать простой старыйint
элемент (ответ: экземпляр в вызове члена была пустая ссылка, такthis
же как и нулевой указатель, и ваш член вычисляется как адрес 8).Так как насчет проверки нулевых ссылок? Вы дали линию
в вашем вопросе. Что ж, это не сработает: согласно стандарту у вас будет неопределенное поведение, если вы разыменовываете нулевой указатель, и вы не можете создать нулевую ссылку без разыменования нулевого указателя, поэтому нулевые ссылки существуют только внутри области неопределенного поведения. Поскольку ваш компилятор может предполагать, что вы не запускаете неопределенное поведение, он может предполагать, что такой вещи, как нулевая ссылка, не существует (даже если он легко выдаст код, который генерирует пустые ссылки!). Таким образом, он видит
if()
условие, делает вывод, что оно не может быть истинным, и просто отбрасывает всеif()
утверждение. С введением оптимизации времени компоновки стало просто невозможно надежно проверять нулевые ссылки.TL; DR:
Нулевые ссылки - это нечто ужасное:
Их существование кажется невозможным (= по стандарту),
но они существуют (= сгенерированным машинным кодом),
но вы не можете их видеть, если они существуют (= ваши попытки будут оптимизированы),
но они в любом случае могут убить вас, не подозревая (= ваша программа вылетает из-за странных моментов или того хуже).
Ваша единственная надежда в том, что их не существует (= напишите свою программу, чтобы не создавать их).
Я очень надеюсь, что это не станет вас преследовать!
источник
Если вы намеревались найти способ представления null в перечислении одноэлементных объектов, то ссылаться на null (это C ++ 11, nullptr) - плохая идея.
Почему бы не объявить статический одноэлементный объект, представляющий NULL в классе, как показано ниже, и не добавить оператор преобразования в указатель, который возвращает nullptr?
Изменить: исправлено несколько опечаток и добавлен оператор if в main (), чтобы проверить, действительно ли работает оператор преобразования в указатель (что я забыл ... моя проблема) - 10 марта 2015 г. -
источник
clang ++ 3.5 даже предупреждает об этом:
источник