Почему нулевой адрес используется для нулевого указателя? - PullRequest
117 голосов
/ 03 мая 2010

В C (или C ++ в этом отношении) указатели являются особыми, если они имеют нулевое значение: я советую устанавливать указатели в ноль после освобождения их памяти, потому что это означает, что освобождение указателя снова не опасно; когда я вызываю malloc, он возвращает указатель со значением ноль, если он не может получить мне память; Я все время использую if (p != 0), чтобы убедиться, что переданные указатели действительны и т. Д.

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


Edit:

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

  • Как и все остальное в программировании, это абстракция. Просто константа, не связанная с адресом 0. C ++ 0x подчеркивает это, добавляя ключевое слово nullptr.

  • Это даже не абстракция адреса, это константа, указанная стандартом C, и компилятор может преобразовать ее в какое-то другое число при условии, что он никогда не будет равняться «реальному» адресу и равен другому нулю указатели, если 0 - не лучшее значение для платформы.

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

  • Я признаю, что мое отрицательное числовое предложение было немного диким мозговым штурмом. Использование целого числа со знаком для адресов немного расточительно, если оно означает, что кроме нулевого указателя (-1 или чего-либо другого) пространство значений равномерно разделено между положительными целыми числами, которые дают действительные адреса, и отрицательными числами, которые просто напрасны.

  • Если какое-либо число всегда представляется типом данных, то оно равно 0. (Вероятно, 1 тоже. Я думаю об одномразрядном целом числе, которое будет 0 или 1, если оно не подписано, или просто бит со знаком, если подписано, или двухбитовое целое число, которое будет [-2, 1]. Но тогда вы можете просто перейти к 0, равному нулю, и 1, являющемуся единственным доступным байтом в памяти.)

Тем не менее, в моем разуме есть что-то нерешенное. Вопрос переполнения стека Указатель на определенный фиксированный адрес говорит мне, что даже если 0 для нулевого указателя является абстракцией, другие значения указателя не обязательно. Это заставляет меня опубликовать еще один вопрос переполнения стека, Могу ли я когда-нибудь получить доступ к нулевому адресу? .

Ответы [ 21 ]

62 голосов
/ 03 мая 2010

2 балла:

  • только постоянное значение 0 в исходном коде является нулевым указателем - реализация компилятора может использовать любое значение, которое оно хочет или нуждается в работающем коде. Некоторые платформы имеют специальное значение указателя, которое «недопустимо», и реализация может использовать его как нулевой указатель. В C FAQ есть вопрос, «Серьезно, есть ли на реальных машинах действительно ненулевые нулевые указатели или разные представления для указателей на разные типы?» , который указывает на несколько платформ, которые использовали это свойство 0, являющееся нулевой указатель в источнике C, хотя он представлен по-разному во время выполнения. В стандарте C ++ есть примечание, в котором четко указывается, что преобразование «выражения с целочисленной константой со значением ноль всегда приводит к нулевому указателю, но преобразование других выражений с нулевым значением не обязательно приводит к нулевому указателю».

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

Единственные требования для нулевого указателя:

  • гарантированно сравнивается неравное с указателем на реальный объект
  • любые два нулевых указателя будут сравниваться одинаково (C ++ уточняет это так, что это нужно только для указателей одного типа)
30 голосов
/ 03 мая 2010

Исторически адресное пространство, начинающееся с 0, всегда было ПЗУ, использовалось для некоторой операционной системы или подпрограмм обработки прерываний низкого уровня, в настоящее время, поскольку все является виртуальным (включая адресное пространство), операционная система может сопоставить любое распределение любому адресу, так что он может специально НЕ размещать что-либо по адресу 0.

15 голосов
/ 03 мая 2010

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

Ни в C, ни в C ++ указатели не могут "иметь нулевое значение". Указатели не являются арифметическими объектами. Они не могут иметь числовые значения, такие как «ноль» или «отрицательный» или что-нибудь в этом роде. Так что ваше утверждение о «указателях ... имеют нулевое значение» просто не имеет смысла.

В C & C ++ указатели могут иметь зарезервированное значение нулевого указателя . Фактическое представление значения нулевого указателя не имеет ничего общего с любыми «нулями». Это может быть абсолютно все, что подходит для данной платформы. Это правда, что на большинстве платформ значение нулевого указателя физически представлено фактическим нулевым значением адреса. Однако, если на какой-либо платформе адрес 0 фактически используется для какой-либо цели (то есть вам может потребоваться создать объекты по адресу 0), значение нулевого указателя на такой платформе, скорее всего, будет другим. Например, он может быть физически представлен как 0xFFFFFFFF адресное значение или 0xBAADBAAD адресное значение.

Тем не менее, независимо от того, как значение нулевого указателя представлено на данной платформе, в вашем коде вы все равно продолжите обозначать нулевые указатели константой 0. Чтобы присвоить значение нулевого указателя данному указателю, вы будете продолжать использовать выражения типа p = 0. Обязанность компилятора - реализовать то, что вам нужно, и перевести его в правильное представление значения нулевого указателя, т.е. преобразовать его в код, который поместит значение адреса 0xFFFFFFFF в указатель p, например.

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

15 голосов
/ 03 мая 2010

IIRC, значение "нулевого указателя" не обязательно будет нулевым. Компилятор переводит 0 в любое «нулевое» значение, подходящее для системы (которое на практике, вероятно, всегда равно нулю, но не обязательно). Тот же перевод применяется всякий раз, когда вы сравниваете указатель с нулем. Поскольку вы можете сравнивать только указатели друг с другом и с этим специальным значением-0, это изолирует программиста от знания чего-либо о представлении памяти в системе. Что касается того, почему они выбрали 0 вместо 42 или что-то подобное, я собираюсь догадаться, что это потому, что большинство программистов начинают считать с 0 :) (Кроме того, в большинстве систем 0 - это первый адрес памяти, и они хотели, чтобы он был удобным, поскольку в практиковать переводы, которые я описываю, на самом деле происходит редко, язык их просто учитывает).

8 голосов
/ 03 мая 2010

Но поскольку адресация памяти начинается с 0, не является ли 0 таким же действительным адресом, как и любой другой?

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

Почему вместо этого отрицательное число не равно нулю?

Я думаю, что значения указателя обычно обрабатываются как числа без знака: в противном случае, например, 32-разрядный указатель сможет адресовать только 2 ГБ памяти вместо 4 ГБ.

5 голосов
/ 04 мая 2010

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

(Большинство ISA устанавливают флаги только для инструкций ALU, но не для загрузки. Однако обычно вы не производите указатели с помощью вычислений, за исключением компилятора при синтаксическом анализе C source . Но по крайней мере вы этого не делаете t нужна произвольная константа ширины указателя для сравнения.)

На Commodore Pet, Vic20 и C64, которые были первыми машинами, на которых я работал, ОЗУ начиналось с местоположения 0, поэтому было вполне допустимо читать и писать, используя нулевой указатель, если вы действительно этого хотели.

3 голосов
/ 03 мая 2010

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

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

3 голосов
/ 03 мая 2010

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

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

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

2 голосов
/ 03 мая 2010

Исторически мало памяти приложения было занято системными ресурсами. В те дни ноль стал нулевым значением по умолчанию.

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

2 голосов
/ 08 ноября 2017

Важная причина, по которой многие операционные системы используют ноль все биты для представления нулевого указателя, заключается в том, что это означает, что memset(struct_with_pointers, 0, sizeof struct_with_pointers) и т. Д. Будут устанавливать все указатели внутри struct_with_pointers в нулевые указатели. Это не гарантируется стандартом C, но многие, многие программы предполагают это.

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