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

Почему NULL/0 является незаконным местом памяти для объекта?

Я понимаю цель константы NULL в C/С++, и я понимаю, что она должна быть представлена ​​каким-то образом внутри.

Мой вопрос: есть ли какая-то фундаментальная причина, почему 0-адрес будет недопустимым местом памяти для объекта в C/С++? Или мы в теории "теряем" один байт памяти из-за этого резервирования?

4b9b3361

Ответ 1

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

char *foo = (void *)1;
--foo;
// do something with foo

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

Изменить: Отредактировано использование NULL из-за путаницы в комментариях. Кроме того, основное сообщение здесь - "null pointer!= 0, а здесь некоторый C/псевдо-код, который показывает точку, которую я пытаюсь сделать". Пожалуйста, на самом деле не пытайтесь скомпилировать это или беспокоиться о том, являются ли типы правильными; смысл ясен.

Ответ 2

Это не имеет ничего общего с потерей памяти и больше с организацией памяти.

Когда вы работаете с пространством памяти, вы должны предположить, что все, что напрямую не принадлежит "Принадлежность к вам", разделяется всей системой или незаконно для вас. Адрес "принадлежит вам", если вы приняли адрес чего-то в стеке, который все еще находится в стеке, или если вы получили его из динамического распределителя памяти и еще не переработали его. Некоторые вызовы ОС также предоставят вам юридические области.

В старые добрые времена реального режима (например, DOS) все начальное пространство адресного пространства машины не предназначалось для написания программ пользователя вообще. Некоторые из них даже сопоставлялись с такими вещами, как I/O. Например, запись в адресное пространство на 0xB800 (довольно низкая) фактически позволит вам захватить экран! На адрес 0 ничего не было, и многие контроллеры памяти не позволяли вам получить к нему доступ, поэтому это был отличный выбор для NULL. На самом деле, контроллер памяти на некоторых ПК пошел бы в шок, если бы вы попробовали написать там.

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

Ответ 3

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

Целочисленное константное выражение со значением 0 или такое выражение, отлитое для типа void *, называется константой нулевого указателя.

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

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

Ответ 4

Нулевой адрес и указатель NULL не являются (обязательно) одним и тем же. Только буквальный ноль - это нулевой указатель. Другими словами:

char* p = 0; // p is a null pointer

char* q = 1;
q--; // q is NOT necessarily a null pointer

Системы могут свободно представлять нулевой указатель внутренне любым способом, который они выбирают, и это представление может или не может "отбросить" байты памяти, сделав фактический 0-адрес незаконным. Однако компилятор должен преобразовать литеральный нулевой указатель во все внутренние представления системы NULL. Указатель, который ссылается на нулевой адрес каким-либо образом, отличным от назначения литерального нуля, необязательно является нулевым.

Теперь большинство систем используют 0 для NULL, но им не нужно.

Ответ 5

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

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

Ответ 6

На многих процессорах адрес 0 - это вектор reset, в котором находится bootrom (BIOS на ПК), поэтому вы вряд ли будете хранить что-либо на этом физическом адресе. На процессоре с MMU и поддерживающей ОС адреса физических и логических адресов не должны быть одинаковыми, а нулевой адрес не может быть допустимым логическим адресом в контексте процесса выполнения.

Ответ 7

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

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

Ответ 8

Я не вижу ответов, непосредственно обращаясь к тому, что, как я думаю, вы спрашивали, так вот:

Да, по крайней мере, 1 адресное значение "потеряно" (оно недоступно для использования) из-за константы, используемой для null. Является ли оно сопоставлением с 0 в линейной карте памяти процесса, не имеет значения.

И причина, по которой этот адрес не будет использоваться для хранения данных, заключается в том, что вам нужен этот особый статус нулевого указателя, чтобы он отличался от любого другого реального указателя. Так же, как в случае строк ASCIIZ (строка C, NUL-terminated), где символ NUL обозначается как конец символьной строки и не может использоваться внутри строк. Вы все еще можете использовать его внутри? Да, но это будет вводить в заблуждение библиотечные функции, когда заканчивается строка.

Я могу представить себе хотя бы одну реализацию LISP, которую я изучал, в которой NIL (Lisp null) не был 0, и не был недопустимым адресом, а реальным объектом. Причина была очень умна - стандарт требовал, чтобы CAR (NIL) = NIL и CDR (NIL) = NIL (Примечание: CAR (l) возвращает указатель на элемент head/first из списка, где CDR (l) возвращает ptr в хвост/остальная часть списка.). Поэтому вместо добавления if-check в CAR и CDR, является ли указатель NIL - который будет замедлять каждый вызов, - они просто выделили CONS (список мысли) и назначили его голову и хвост, чтобы указать на себя. Там! - таким образом CAR и CDR будут работать, и этот адрес в памяти не будет использоваться повторно (поскольку он берется объектом, разработанным как NIL)

пс. я только что вспомнил, что много-много лет назад я читал о некоторой ошибке Lattice-C, связанной с NULL, - должно быть, было в темное время сегментации MS-DOS, где вы работали с отдельным сегментом кода и сегментом данных, - поэтому я помню была проблема, связанная с тем, что первая функция из связанной библиотеки имела адрес 0, поэтому указатель на нее будет считаться недействительным, так как == NULL

Ответ 9

Но так как современные операционные системы могут отображать физическую память на адреса логической памяти (или лучше: современные процессоры, начиная с 386), ни один байт не теряется.

Ответ 10

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

Ответ 11

Вы правильно заметили, что адресное пространство в 0 не используется для хранения вашей программы. По ряду причин множество систем не считают это действительным адресным пространством для вашей программы в любом случае.

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

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

Ответ 12

Память по этому адресу зарезервирована для использования операционной системой. 0 - 64k зарезервировано. 0 используется как специальное значение, чтобы указать разработчикам "недействительный адрес".