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

Почему разыменование нулевого указателя - это поведение undefined?

В соответствии с ISO С++ разыменование нулевого указателя имеет поведение undefined. Мое любопытство, почему? Почему стандарт решил объявить это поведение undefined? В чем смысл этого решения? Зависимость компилятора? Не похоже, потому что согласно стандарту C99, насколько я знаю, он четко определен. Зависимость машины? Любые идеи?

4b9b3361

Ответ 1

Определение согласованного поведения при разыменовании указателя NULL потребует от компилятора проверки указателей NULL перед каждым разыменованием на большинстве архитектур процессора. Это неприемлемый термин для языка, который предназначен для скорости.

Он также фиксирует только небольшую часть более крупной проблемы - существует много способов иметь недопустимый указатель за указателем NULL.

Ответ 2

Основная причина заключается в том, что к тому времени, когда они написали исходный стандарт C, было реализовано несколько реализаций, которые разрешили его, но дали противоречивые результаты.

На PDP-11 произошло, что адрес 0 всегда содержал значение 0, поэтому разыменование нулевого указателя также дало значение 0. Довольно много людей, которые использовали эти машины, считали, что, поскольку они были оригинальной машиной C, написанное/используемое для программирования, что это должно рассматриваться как каноническое поведение для C на всех машинах (хотя это изначально произошло довольно случайно).

На некоторых других машинах (Interdata приходит на ум, хотя моя память может быть ошибочной) адрес 0 был нормализован, поэтому он мог содержать другие значения. Было также некоторое аппаратное обеспечение, на котором адрес 0 был фактически некоторым аппаратным обеспечением, связанным с памятью, поэтому чтение/запись выполняло особые вещи - совсем не эквивалентно чтению/записи нормальной памяти вообще.

Лагеря не согласятся с тем, что должно произойти, поэтому они сделали это undefined.

Изменить: я полагаю, я должен добавить, что к моменту написания стандарта С++ его поведение undefined было уже хорошо установлено в C, и (по-видимому) никто не думал, что есть веская причина для создания конфликта на этом поэтому они сохраняют то же самое.

Ответ 3

Единственный способ дать определенное поведение - добавить проверку выполнения во все разыменования указателя и любую арифметическую операцию указателя. В некоторых ситуациях эти накладные расходы были бы неприемлемыми и сделали бы С++ непригодным для высокопроизводительных приложений, для которых он часто использовался.

С++ позволяет создавать собственные типы интеллектуальных указателей (или использовать те, которые предоставляются библиотеками), которые могут включать такую ​​проверку в случаях, когда безопасность важнее производительности.

Выделение нулевого указателя также undefined в C, в соответствии с разделом 6.5.3.2/4 стандарта C99.

Ответ 4

Этот ответ от @Johannes Schaub-litb, выдвигает интересное обоснование, которое кажется довольно убедительным.


Формальная проблема с просто разыменованием нулевого указателя заключается в том, что определение идентичности полученного выражения lvalue не представляется возможным: каждое такое выражение, которое возникает в результате разыменования указателя, должно однозначно ссылаться на объект или функцию, когда это выражение оценивается. Если вы разыскиваете нулевой указатель, у вас нет объекта или функции, которые идентифицирует это значение lvalue. Это аргумент, используемый стандартом для запрета нулевых ссылок.

Еще одна проблема, которая добавляет к путанице, состоит в том, что семантика оператора typeid делает эту проблему плохо определенной. В нем говорится, что если ему была присвоена lvalue, которая возникла в результате разыменования нулевого указателя, в результате возникает исключение bad_typeid. Хотя, это ограниченная область, где существует исключение (без каламбура) для вышеупомянутой проблемы поиска идентичности. Существуют и другие случаи, когда выполняется аналогичное исключение из поведения undefined (хотя и гораздо менее тонкое и со ссылкой на затронутые разделы).

Комитет обсудил эту проблему в глобальном масштабе, определив тип lvalue, который не имеет идентификатора объекта или функции: так называемое пустое значение lvalue. Однако у этой концепции все еще были проблемы, и они решили не принимать ее.


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

Ответ 5

Реальный вопрос: какое поведение вы ожидаете?

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

Итак, как вы получаете хорошую ссылку... из указателя, который указывает на пустоту?

Нет. Таким образом, поведение undefined.

Ответ 6

Я подозреваю, что, если поведение корректно определено, компилятор должен вставлять код, в любом месте указатели разыменовываются. Если бы это была реализация, то одно возможное поведение по-прежнему может быть тяжелым. Если это не указано, то либо компиляторы для некоторых систем имеют дополнительное неоправданное бремя, либо могут генерировать код, который вызывает жесткие сбои.

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

Ответ 7

Иногда вам нужен недопустимый указатель (также см. MmBadPointer в Windows), чтобы представлять "ничего".

Если бы все было действительно, это было бы невозможно. Поэтому они сделали недействительным NULL и запретили вам разыгрывать его.

Ответ 8

В соответствии с исходным стандартом C NULL может быть любое значение - не обязательно ноль.

В определении языка указано, что для каждого типа указателя существует специальное значение - "нулевой указатель" - которое отличается от всех других значений указателя и которое "гарантировано сравнивается неравномерно с указателем на любой объект или функцию. ' То есть нулевой указатель указывает окончательно нигде; это не адрес любого объекта или функции

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

(Из http://c-faq.com/null/null1.html)

Ответ 9

Вот простой пример и пример:

  • Выделите указатель:

    int * pointer;

? Какое значение находится в указателе при его создании?
? На что указывает указатель?
? Что происходит, когда я разыгрываю эту точку в ее текущем состоянии?

  • Отметить конец связанного списка. В связанном списке a node указывает на другой node, за исключением последнего.
    Каково значение указателя в последнем node?
    Что произойдет, если вы отмените "следующее" поле последнего node?

Необходимо указать значение, указывающее на то, что указатель не указывает на что-либо или что он находится в недопустимом состоянии. Именно здесь вступает в действие концепция указателя NULL. Связанный список может использовать указатель NULL для указания конца списка.

Ответ 10

Хотя разыменование указателя NULL в C/С++ действительно приводит к поведению undefined с точки зрения языка, такая операция хорошо определена в компиляторах для целей, имеющих память с соответствующим адресом. В этом случае результат такой операции состоит в простом считывании памяти по адресу 0.

Кроме того, многие компиляторы позволят вам разыменовать указатель NULL, пока вы не привязываете ссылочное значение. Это делается для обеспечения совместимости с несовместимым, но распространенным кодом, например

#define offsetof(st, m) ((size_t)(&((st *)0)->m))

Было даже обсуждение , чтобы сделать это поведение частью стандарта.

Ответ 11

Потому что вы не можете создать нулевую ссылку. С++ не позволяет этого. Поэтому вы не можете разыменовать нулевой указатель.

В основном это undefined, потому что нет логического способа справиться с этим.