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

Вызов указателя на 0 в C

Иногда данные по адресу памяти 0x0 весьма ценны - возьмите X86 real mode IVT как более известный пример: он начинается с 0x0 и содержит указатели на прерывания обработчиков: dword at 0x00 является указателем на деление на нулевой обработчик ошибок.

Однако стандарт языка C11 запрещает разыменовывать нулевые указатели [WG14 N1570 6.5.3.2], которые определяются как указатели, инициализированные нулевыми или указательными инициализированными с нулевым указателем [WG14 N1570 6.3.2.3], фактически запрещающим самый первый байт.

Как люди действительно используют 0x0, когда это необходимо?

4b9b3361

Ответ 1

C не запрещает разыменование нулевого указателя, он просто делает его undefined.

Если ваша среда такова, что вы можете разыменовать указатель, содержащий адрес 0x0, тогда вы сможете это сделать. Стандарт языка C ничего не говорит о том, что произойдет, когда вы это сделаете. (И в большинстве сред, результатом будет сбой программы.)

Конкретный пример (если я правильно помню это): на компьютерах Sun 3 на базе 68k разыменование нулевого указателя не вызывало ловушку; вместо этого ОС сохранила нулевое значение в нулевом адресе памяти, а разыменование нулевого указателя (указывающего на нуль адреса) даст это нулевое значение. Это означало, например, что программа C могла рассматривать нулевой указатель как допустимый указатель на пустую строку. Некоторое программное обеспечение, преднамеренно или нет, зависело от этого поведения. Это потребовало большой очистки при переносе программного обеспечения на SPARC-основанное Sun 4, которое было захвачено нумерацией нулевых указателей. (Я отчетливо помню, как читал об этом, но мне не удалось найти ссылку, я обновлю это, если найду его.)

Обратите внимание, что нулевой указатель необязательно равен нулю; точнее, представление нуля может быть или не быть битом-ноль. Это очень часто, но это не гарантируется. (Если это не так, преобразование целых чисел в указатель (void*)0 является нетривиальным.)

Раздел 5 comp.lang.c FAQ обсуждает нулевые указатели.

Ответ 2

Как люди действительно используют 0x0, когда это необходимо?

С помощью:

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

Ответ 3

Заявление:

char * x = 0;

не обязательно устанавливает 0x0 в x. Он помещает определенное значение нулевого указателя для текущей архитектуры и компилятора в x.

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

Ответ 4

Приложение J Это поведение undefined, когда...

Операнд унарного * оператора имеет недопустимое значение (6.5.3.2).

В той же сноске, о которой вы упомянули, он говорит, что нулевой указатель является недопустимым значением. Поэтому это не запрещено, но undefined поведение. Что касается различия между адресом 0x0 и нулевым указателем, см. Является ли адрес памяти 0x0 полезным?.

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

Указывает ли пустой указатель на оперативную систему или Реализация С++ не определена, но простой новый никогда не вернет нулевой указатель, независимо от его адреса (nothrow new - это другое зверь). Итак, чтобы ответить на ваш вопрос:

Можно ли использовать адрес памяти 0x0?

Возможно, это зависит от конкретной реализации/архитектуры.

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

Ответ 5

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

Итак, когда вы делаете

char *ptr = 0x0; 

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