Я спрашиваю из-за обсуждения, которое было вызвано в этой теме.
Попытка провести серьезную дискуссию по-назад, используя комментарии под другими людьми, не просто и не весело. Поэтому я хотел бы услышать, что думают наши эксперты C, не ограничиваясь 500 персонажами за раз.
В стандарте C есть несколько слов, которые можно сказать о NULL
и константах нулевого указателя. Там только два соответствующих раздела, которые я могу найти. Во-первых:
3.2.2.3 Указатели
Интегральное постоянное выражение с значение 0 или такое выражение cast to type void *, называется нулевым постоянная указателя. Если нулевой указатель константа присваивается или сравнивается для равенства указателю, константа преобразуется в указатель этот тип. Такой указатель, называемый null указатель, гарантируется сравнение не соответствует указателю на какой-либо объект или функция.
и второй:
4.1.5 Общие определения
Макросы
NULL
который расширяется до константы нулевого указателя, определяемой реализацией;
Вопрос в том, может ли NULL
развернуть до константы указателя нулевой указателя, которая отличается от тех, которые перечислены в 3.2.2.3?
В частности, можно ли это определить как:
#define NULL __builtin_magic_null_pointer
Или даже:
#define NULL ((void*)-1)
Мое чтение 3.2.2.3 состоит в том, что он указывает, что интегральное постоянное выражение 0 и целочисленное постоянное выражение 0, передаваемого типу void *, должны быть среди форм константы нулевого указателя, которые реализует реализация, но что это не является исчерпывающим списком. Я считаю, что реализация может распознавать другие исходные конструкции как константы нулевого указателя, если никакие другие правила не нарушаются.
Так, например, доказывается, что
#define NULL (-1)
не является юридическим определением, поскольку в
if (NULL)
do_stuff();
do_stuff()
не следует вызывать, тогда как
if (-1)
do_stuff();
do_stuff()
должен быть вызван; поскольку они эквивалентны, это не может быть юридическим определением NULL
.
Но в стандарте говорится, что преобразования целых-на-указателей (и наоборот) являются определяемыми реализацией, поэтому он может определять преобразование -1 в указатель как преобразование, которое создает нулевой указатель. В этом случае
if ((void*)-1)
будет оцениваться как false, и все будет хорошо.
Так что думают другие люди?
Я прошу всех особенно помнить правило "как есть", описанное в 2.1.2.3 Program execution
. Это огромная и несколько круглая, поэтому я не буду вставлять ее здесь, но по сути говорит, что реализация просто должна производить те же наблюдаемые побочные эффекты, какие требуются от абстрактной машины, описанной в стандарте. В нем говорится, что любые оптимизации, преобразования или что-то еще, что компилятор хочет сделать с вашей программой, совершенно легальны, пока наблюдаемые побочные эффекты программы не будут изменены ими.
Итак, если вы хотите доказать, что конкретное определение NULL
не может быть законным, вам нужно придумать программу, которая может это доказать. Либо один, как мой, который нагло нарушает другие предложения в стандарте, или тот, который может юридически обнаружить любую магию, которую должен выполнить компилятор, чтобы сделать странное определение NULL.
Стив Джессоп нашел пример того, как программа обнаруживает, что NULL
не определена как одна из двух форм констант нулевого указателя в 3.2.2.3, которая заключается в том, чтобы выровнять константу:
#define stringize_helper(x) #x
#define stringize(x) stringize_helper(x)
Используя этот макрос, можно было
puts(stringize(NULL));
и "обнаружить", что NULL не распространяется на одну из форм в 3.2.2.3. Достаточно ли этого сделать другие определения незаконными? Я просто не знаю.
Спасибо!