Может ли соответствующая реализация C #define NULL быть чем-то дурацким

Я спрашиваю из-за обсуждения, которое было спровоцировано вэта тема.

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

В стандарте C есть несколько слов оNULL и константы нулевого указателя. Есть только два соответствующих раздела, которые я могу найти. Первый:

3.2.2.3 Указатели

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

и второе:

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) 

оценили бы как ложное, и все было бы хорошо.

Так что думают другие люди?

Я бы попросил всех особо помнить правило «как будто», описанное в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. Достаточно ли этого, чтобы сделать другие определения незаконными? Я просто не знаю.

Спасибо!

Ответы на вопрос(6)

Ваш ответ на вопрос