Какая часть разыменования указателей NULL вызывает нежелательное поведение?
===========================================================================

Мне интересно, какая часть разыменования NULL ptr вызывает нежелательное поведение. Пример:

Здесь в # 2 мы видим, что я на самом деле не пытался получить доступ к данным или функции из b, поэтому будет ли это вызывать нежелательное поведение, если * b просто преобразуется в NULL и мы передаем NULL в anotherfunc ()?

 потому что вы по-прежнему указываете компилятору попытаться получить доступ к памяти в местоположении 0 (что запрещено). В зависимости от подписи`anotherfunc`, вы можете передавать ссылку (которую запрещено инициализировать нулевым объектом), илиcopy из`*b`.



 но это не обязательно нежелательное поведение. Часть полезности`NULL` в том, что на большинстве платформ он указывает на память, которая явно недоступна для вашего приложения, и вызывает ошибку сегментации (или нарушение доступа) в тот самый момент, когда вы пытаетесь разыменовать ее.

Его цель - явно пометить содержимое указателей как недействительные.



пределенным поведением, современные процессоры и операционные системы генерируют ошибку сегментации или подобную ошибку.

Может быть, та функция, которую вы вызвали, принимает ссылочный параметр (который является указателем), и эта функция не использует этот параметр, так что значение NULL не будет разыменовано.



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

(Это не гарантируется стандартом, хотя он работает таким образом на всех компиляторах, с которыми я когда-либо сталкивался)



 чтобы сказать, что произойдет, когда вы передадите его null. это может быть хорошо, это может произойти сбой, зависит от кода.



 я не правильно инициализировал, поэтому вопрос в лучшем случае неоднозначный, но большинство всех прямо ответили на мой вопрос, я невольно задал вопрос, пока не вошел в систему (извините, я новичок в stackoverflow), так что может кто-то с редактированием полномочия обновить ОП?



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

Имя значения нулевого указателя`0`или любое другое постоянное интегральное выражение в контексте указателя (например,`3 - 3`, например). Также есть`NULL` макрос, который должен быть равен 0 в C ++, но может быть`(void *)0` в C (C ++ настаивает на большей безопасности указателей). В C ++ 0x будет явное значение, называемое`nullptr`наконец, давая нулевому указателю явное имя.

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

Вы неверно понимаете, что происходит в вашем последнем примере.`*b` не разрешается ни во что. Переходя`*b` является неопределенным поведением, что означает, что реализация может делать с ним все что угодно. Это может или не может быть помечено как ошибка, и может вызвать или не вызвать проблемы. Поведение может измениться без видимой причины, и это делает ошибку.

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



Не гарантируется сбой, и вы ничего не гарантируете при этом. Ибо все, что вы знаете, кто-то где-нибудь в мире будет бить каждый раз, когда вы это делаете. Это правильное поведение, поскольку оно не определено.

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



 да. Вам разрешено вызывать функции-члены только для допустимого объекта. И нулевой указатель не указывает на действительный объект.

Причина, почему этоappears работать, это то, что функции-члены обычно реализуются примерно так:

То есть «это» указатель в основном передается функции как отдельный аргумент. Таким образом, при вызове функции компилятор не проверяет, что`this` Указатель действителен, он просто передает его в функцию.

Это все еще неопределенное поведение, хотя. Компилятор не гарантирует, что это сработает.



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

Так что простой вызов функции-члена для указателя объекта NULL не обязательно вызывает сбой (если только это не виртуальная функция). Вы получаете неверный доступ к памяти при сбое, только когда пытаетесь получить доступ к переменным-членам объекта или vtable.

В случае № 2 вы можете или не можете получить немедленный сбой, в зависимости от того, как`anotherfunc` объявлен Если это займет`someObj` по значению вы косвенно обращаетесь к NULL в самом вызове функции, что приводит к сбою. Если это займет`someObj` по ссылке, обычно ничего не происходит, так как ссылки реализуются с помощью указателей под капотом, а фактическое косвенное обращение откладывается до тех пор, пока вы не попытаетесь получить доступ к данным члена.



Если anotherfunc () принимает ссылку на b, то вы не отменили ссылку на b, вы просто преобразовали ее в ссылку. Если, с другой стороны, это параметр значения, то будет вызван конструктор копирования, а затем вы отменили ссылку на него.

Погода, в которой он потерпит крах, будет зависеть от многих факторов (например, если у него есть участники). Но действие отмены ссылки на NULL не определено, поэтому он может работать на вашем компиляторе.

Что касается первого варианта вызова метода по нулевому указателю.  

Это также неопределенное поведение. Погода, в которой он падает, будет зависеть от компилятора и ОС. Но это совершенно справедливо, чтобы не потерпеть крах (поведение не определено).

Большая путаница возникает из-за того, что люди ссылаются на * in * b как оператор разыменования. Это может быть его обычное имя, но в стандарте это «унарный * оператор» и это определяется как:

 5.3.1
 
 The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

Таким образом, «унарный * оператор» возвращает ссылку на объект, на который указывал указатель, к которому он был применен. (На данный момент не было разыменования).



`NULL` значение. Это означает, что вы можете вызывать не виртуальные функции, потому что они связаны во время компиляции. Он вызывает функцию просто отлично и проходит в`NULL` `this` указатель. Теперь, если вы попытаетесь использовать какие-либо переменные-члены, произойдет сбой, потому что он попытается найти их на основе`this` указатель передан внутрь. Вы также можете вызывать другие не виртуальные функции по тому же аргументу. Теперь, если вы попытаетесь использовать виртуальную функцию, она сразу же потерпит крах, поскольку попытается найти`vtable` от`NULL` указатель.

Мы столкнулись с подобным случаем, и мне пришлось написать некоторый пример кода, чтобы продемонстрировать другим разработчикам, что, несмотря на то, что он сообщал об ошибке в двух уровнях вызовов функций-членов, на самом деле это был`NULL` указатель, который вызывался. Ошибка проявлялась при использовании фактического значения.



 что во многих случаях было бы неплохо, если бы вызывался экземпляр функции на`null` привело к`null`, Однако я не думаю, что это должно быть по умолчанию. Вместо этого должен быть другой оператор (я оставлю то, что это решать кому-то другому, но давайте скажем это).`->>`).

Например, одной из проблем в C ++ является то, что не все возвращаемые типы могут быть`null` во всяком случае, такие как`int`, Так что призыв к`a->>length()` было бы сложно узнать, что вернуть, когда`a` сам был`null`.

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

Наконец, Бак, все остальные говорят о том, как обстоят дела, особенно для языка C ++: разыменование - это механическая операция в большинстве языков: она должна возвращать что-то того же типа и`null` обычно хранится как ноль. Старые системы просто зависали, когда вы пытались разрешить ноль, более новые системы распознавали бы особый характер значения при возникновении ошибки.

Кроме того, эти языки более низкого уровня не могут представлять`null` как целое число (или другие основные типы данных), так что вы не могли бы вообще почтить`null` как`null` во всех случаях.



дения памяти. Однажды в голове какого-то умного программиста загорается лампочка. Он сказал: «Что если я сделаю незаконным доступ к первой странице памяти и наведу на нее все недействительные указатели?» Как только это произошло, большинство ошибок повреждения памяти были быстро обнаружены.

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



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

Этоis неопределенное поведение указано в различных примечаниях по всему стандарту. Но примечания не являются нормативными: они могут сказать что угодно, но никогда не смогут сформулировать какие-либо правила. Их цель полностью информативна.

То, что вызов функции-члена по нулевому указателю формально, также является неопределенным поведением.

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

Другая проблема, которая добавляет путаницу в том, что семантика`typeid` Оператор делает часть этого страдания хорошо определенной. Он говорит, что если ему было дано значение l, которое возникло из-за разыменования нулевого указателя, результат бросает`bad_typeid` исключение. Хотя это ограниченная область, где существует исключение (не каламбур) для вышеуказанной проблемы поиска личности. Существуют и другие случаи, в которых делается схожее исключение с неопределенным поведением (хотя и гораздо менее тонкое и со ссылкой на затронутые разделы).

Комитет обсудил, чтобы решить эту проблему глобально, определив вид lvalue, у которого нет идентификатора объекта или функции: так называемыйempty lvalue, Однако у этой концепции все еще были проблемы, и они решили[не принять его](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232).

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

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

Поскольку чтение из массива просто возвращает его адрес с использованием типа указателя элемента, это, скорее всего, просто создаст нулевой указатель (но поскольку вы разыменовываете нулевой указатель ранее, это все еще формально неопределенное поведение). Другой случай - разыменование нулевых указателей на функции. Здесь также чтение функции lvalue просто даст вам ее адрес, но с использованием типа указателя на функцию:

Как и в других случаях, это тоже неопределенное поведение, но, вероятно,not привести к аварии.

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

Перед некоторыми телами функций-членов на случай, если они случайно вызвали функцию с нулевым указателем. Это может быть хорошей идеей, когда часто бывают случаи, когда такие функции по ошибке вызываются на нулевых указателях для раннего обнаружения ошибок. Но с формальной точки зрения,`this` никогда не может быть нулевым указателем в функции-члене.