Почему ПУСТОЙ УКАЗАТЕЛЬ/0 является недопустимой ячейкой памяти для объекта?

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

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

16
задан aioobe 2 June 2010 в 18:36
поделиться

12 ответов

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

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

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

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

21
ответ дан 30 November 2019 в 15:14
поделиться

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

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

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

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

11
ответ дан 30 November 2019 в 15:14
поделиться

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

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

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

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

8
ответ дан 30 November 2019 в 15:14
поделиться

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

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

2
ответ дан 30 November 2019 в 15:14
поделиться

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

1
ответ дан 30 November 2019 в 15:14
поделиться

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

0
ответ дан 30 November 2019 в 15:14
поделиться

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

1
ответ дан 30 November 2019 в 15:14
поделиться

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

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

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

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

Сейчас большинство систем используют 0 вместо NULL, но это не обязательно.

7
ответ дан 30 November 2019 в 15:14
поделиться

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

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

6
ответ дан 30 November 2019 в 15:14
поделиться

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

4
ответ дан 30 November 2019 в 15:14
поделиться

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

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

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

1
ответ дан 30 November 2019 в 15:14
поделиться

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

Да, по крайней мере одно значение адреса «потеряно» (стало недоступным для использования), потому что константы, используемой для null . Отображается ли он в 0 на линейной карте памяти процесса, не имеет значения.

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

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

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

2
ответ дан 30 November 2019 в 15:14
поделиться