Почему NULL / 0 является недопустимым местом в памяти для объекта? - PullRequest
14 голосов
/ 02 июня 2010

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

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

Ответы [ 12 ]

21 голосов
/ 02 июня 2010

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

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

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

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

11 голосов
/ 02 июня 2010

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

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

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

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

8 голосов
/ 02 июня 2010

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

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

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

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

7 голосов
/ 02 июня 2010

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

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

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

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

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

6 голосов
/ 02 июня 2010

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

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

4 голосов
/ 02 июня 2010

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

2 голосов
/ 05 июня 2010

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

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

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

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

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

2 голосов
/ 02 июня 2010

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

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

1 голос
/ 03 июня 2010

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

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

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

1 голос
/ 03 июня 2010

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

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