it-roy-ru.com

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

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

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

54
chi42

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

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


  1. См. C99, 6.3.2.3.
  2. Там нет явных претензий; но смотрите сноску для C99, 7.20.3 (спасибо @birryree в комментариях) . </ sub>
50
Oliver Charlesworth

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

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

12
John Bode

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

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

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

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

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

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

12
johannes

В 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 * являются not неявно взаимозаменяемыми при передаче через нетипизированный список аргументов, даже если они есть везде.

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

C++ более сложный, и я не чувствую себя способным объяснить, как.

7
zwol

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

0
pizza