NULL всегда ноль в C? - PullRequest
       47

NULL всегда ноль в C?

55 голосов
/ 27 марта 2012

Вчера я брал интервью у парня, занимающего должность инженера-программиста среднего уровня, и он упомянул, что в C NULL не всегда равен нулю и что он видел реализации C, где NULL не равен нулю. Я нахожу это очень подозрительным, но я хочу быть уверен. Кто-нибудь знает, прав ли он?

(Ответы не повлияют на мое суждение по этому кандидату, я уже передал свое решение своему менеджеру.)

Ответы [ 5 ]

54 голосов
/ 27 марта 2012

Я предполагаю, что вы имеете в виду нулевой указатель. Он гарантированно сравнивается равным 0. 1 Но его не обязательно представлять с нулевыми битами. 2

См. Также comp.lang.c FAQ по нулевым указателям.


См. C99, 6.3.2.3. Нет явных претензий; но смотрите сноску для C99, 7.20.3 (спасибо @birryree в комментариях).
13 голосов
/ 27 марта 2012

Нулевой указатель константа всегда равен 0. Макрос NULL может быть определен реализацией как голый 0, или выражение приведения типа (void *) 0, или какое-либо другое значение с нулевым значением целочисленное выражение (следовательно, «определенный реализацией» язык в стандарте).

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

11 голосов
/ 27 марта 2012

§ 6.3.2.3 стандарта C99 гласит

Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя) Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнивает неравные на указатель на любой объект или функцию.

§ 7.17 также говорит

[...] NULL, который расширяется до определенной в реализации постоянной нулевого указателя [...]

Адрес указателя NULL может отличаться от 0, хотя он будет вести себя так же, как и в большинстве случаев.

(Это должно быть так же, как в более старых стандартах C, которых у меня сейчас нет под рукой)

7 голосов
/ 27 февраля 2015

В C существует один и только один контекст, в котором необходимо явно привести константу нулевого указателя к определенному типу указателя, чтобы программа работала правильно. Этот контекст передает нулевой указатель через нетипизированный список аргументов функции. В modern C это происходит только тогда, когда вам нужно передать нулевой указатель на функцию, которая принимает переменное число аргументов. (В устаревшем C это происходит с любой функцией, не объявленной с прототипом.) Примером парадигмы является execl, где самый последний аргумент должен быть нулевым указателем, явно приведенным к (char *):

execl("/bin/ls", "ls", "-l", (char *)0);    // correct
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose

execl("/bin/ls", "ls", "-l", 0);            // undefined behavior
execl("/bin/ls", "ls", "-l", NULL);         // ALSO undefined behavior

Да, последний пример имеет неопределенное поведение , даже если NULL определено как ((void *)0), потому что void * и char * являются , а не , неявно взаимозаменяемыми при передаче через нетипизированный список аргументов, даже если они есть везде.

«Под капотом», проблема здесь в , а не только с битовой комбинацией, используемой для нулевого указателя, но в том, что компилятору может понадобиться знать точный конкретный тип каждого аргумента, чтобы установить правильно создать кадр вызова. (Рассмотрим MC68000 с его отдельными адресами и регистрами данных; некоторые ABI указали аргументы указателя, которые должны быть переданы в адресных регистрах, но целочисленные аргументы в регистрах данных. Рассмотрим также любой ABI, где int и void * не имеют одинаковый размер. в наши дни это невероятно редко, но C по-прежнему явно указывает, что void * и char * не имеют одинаковый размер.) Если есть прототип функции, компилятор может использовать его, но функции без прототипов и аргументы с переменными параметрами не оказывают такой помощи.

C ++ более сложный, и я не могу объяснить, как это сделать.

0 голосов
/ 27 марта 2012

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

...