2012-03-27 5 views
47

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

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

+0

http://bytes.com/topic/c/answers/213647-null-c – soandos

+0

http://www.stackoverflow.com/questions/459743/is-null-always-false – mcabral

+1

http: // c- faq.com/null/index.html –

ответ

44

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

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


  1. См C99, 6.3.2.3.
  2. Нет явных требований; но см. сноску для C99, 7.20.3 (спасибо @birryree в комментариях).
+5

В качестве дополнения к вашей ссылке C99, поскольку «NULL» всегда сравнивается с «0» (константа нулевого указателя), в разделе 7.20.3.2 также отмечается, что все биты, установленные на ноль (например, случай для 'calloc'), не являются обязательно то же, что и представление «0.0f» или целое число «0». – birryree

+0

Awesome. Так что в наши дни это довольно распространенная практика, но не требование. Благодарю. – chi42

+0

Итак, если бы я должен был наложить указатель NULL на int, а затем распечатать его, я бы не печатал '0'? Означает ли это, что компилятор должен помнить, когда он выполняет сравнение указателей, чтобы он мог сравнивать NULL-указатель равным нулю? – chi42

10

§ 6.3.2.3 стандарта C99 говорит

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

§ 7,17 также говорит

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

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

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

10

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

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

+1

Цвет меня глупый, но я боюсь, мне нужно больше образования сейчас. Если у вас есть указатель 'ptr', это не' ptr == NULL' _exactly_ то же самое, что 'ptr == 0' или'! Ptr'? Является ли эта реализация зависимой? –

+3

@MrLister: В контексте вашего * исходного кода вы абсолютно правы - 'ptr == 0' и' ptr == NULL' и '! Ptr' эквивалентны.Однако, если исходный код был переведен на машинный код, фактическое значение нулевого указателя может быть чем-то отличным от 0 (и все эти сравнения будут против этого фактического значения нулевого указателя). –

+0

'Значение нулевого указателя может быть чем-то отличным от 0' - вы имеете в виду' * ptr == something', даже если 'void * ptr = 0'? –

0

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

7

В 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++ сложнее, и я не чувствую себя способным объяснить, как это сделать.

Смежные вопросы