Является ли (int *)0 нулевым указателем?

Это можно рассматривать как расширение этого вопроса (меня интересует только C, но я добавляю C++ для завершения расширения)

Стандарт C11 в 6.3.2.3.3 гласит:

Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя.

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

Но стандарт тогда продолжается:

Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем,...

который охватывает (int *)0 как нулевой указатель, поскольку приведение является явным преобразованием (C11, 6.3), которое указано в разделе методов преобразования.

Однако, что все еще заставляет меня задуматься, так это следующая фраза

... или такое выражение, приведенное к типу void *...

С вышеуказанной семантикой эта фраза кажется совершенно бесполезной. Вопрос в том, является ли эта фраза совершенно бесполезной? Если нет, какие последствия это имеет? Следовательно, (int *)0 нулевой указатель или нет?


Другой вопрос, который может помочь обсуждению, заключается в следующем. Является (long long)123 считается "123 преобразуется в long long"или" 123 с типом long long". Другими словами, есть ли преобразование в (long long)123? Если нет, то вторая цитата выше не распространяется (int *)0 как нулевой указатель.

2 ответа

Решение

Короткий ответ:

И в C, и в C++ (int *)0 константное выражение, значение которого является нулевым указателем. Однако это не константа нулевого указателя. Единственное наблюдаемое различие между константным выражением, чье значение-это-нулевой указатель и константой нулевого указателя, о котором я знаю, состоит в том, что константу нулевого указателя можно присвоить l-значению любого тип указателя, но указатель-константа-выражение-значения-которого-ноль-указатель имеет определенный тип указателя и может быть назначен только lvalue с совместимым типом. В C, но не в C++, (void *)0 также является константой нулевого указателя; это особый случай для void * в соответствии с общим правилом C-но-не-C++, что void * совместимо ли присвоение с любым другим типом указатель на объект.

Например:

long *a = 0;           // ok, 0 is a null pointer constant
long *b = (long *)0;   // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0;   // ok in C, invalid conversion in C++
long *d = (int *)0;    // invalid conversion in both C and C++

И вот случай, когда разница между константой нулевого указателя (void *)0 и константное выражение-чье-значение-это-нулевой указатель с типом void * видно даже в C:

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp a = 0;                  // ok, null pointer constant
fp b = (void *)0;          // ok in C, invalid conversion in C++
fp c = (void *)(void *)0;  // invalid conversion in both C and C++

Кроме того, в настоящее время это спорный вопрос, но с тех пор, как вы подняли этот вопрос: неважно, что за битое представление long *нулевой указатель, все эти утверждения ведут себя так, как указано в комментариях:

// 'x' is initialized to a null pointer
long *x = 0;

// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);

assert (x == 0);         // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified in C++
assert (x == (int *)0);  // must succeed in C, unspecified in C++

assert (memcmp(&x, &y, sizeof y) == 0); // unspecified

assert (y == 0);         // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x);         // UNDEFINED BEHAVIOR: y may be a trap representation

"Неуказанные" сравнения не вызывают неопределенного поведения, но в стандарте не говорится, оценивают ли они значение "истина" или "ложь", и реализация не обязана документировать, какой из двух он является, или даже выбрать одно и придерживаться его. Это было бы совершенно справедливо для вышеизложенного memcmp переключаться между возвратом 0 и 1, если вы вызывали его много раз.


Длинный ответ со стандартными кавычками:

Чтобы понять, что такое константа нулевого указателя, вы должны сначала понять, что такое выражение целочисленной константы, и это довольно странно - для полного понимания необходимо подробно прочитать разделы 6.5 и 6.6 C99. Это мое резюме:

  • Константное выражение - это любое C-выражение, которое компилятор может вычислить до константы, не зная значения какого-либо объекта (const или иным образом; тем не мение, enum значения являются честной игрой), и которая не имеет побочных эффектов. (Это резкое упрощение примерно 25 страниц стандартного языка и может быть неточным.)

  • Целочисленные константные выражения - это ограниченное подмножество константных выражений, удобно определяемое в одном абзаце, C99 6.6p6 и его сноска:

    Целочисленное константное выражение96 должно иметь целочисленный тип и иметь только те операнды, которые являются целочисленными константами, константами перечисления, символьными константами, sizeof выражения, результатом которых являются целочисленные константы и плавающие константы, которые являются непосредственными операндами приведений. Операторы приведения в выражении с целочисленной константой должны преобразовывать только арифметические типы в целочисленные типы, кроме как как часть операнда в sizeof оператор.

    96 Выражение из целочисленной константы используется для указания размера элемента битового поля структуры, значения константы перечисления, размера массива или значения константы регистра. Дополнительные ограничения, которые применяются к целочисленным константным выражениям, используемым в [#if] обсуждаются в 6.10.1.

    Для целей этого обсуждения важный бит

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

    Который означает, что (int *)0 не является целочисленным константным выражением, хотя это константное выражение.

Определение C++98 представляется более или менее эквивалентным, по модулю C++ функции и отклонения от C. Например, более сильное разделение символьных и логических типов от целочисленных типов в C++ означает, что стандарт C++ говорит о "интегральных константных выражениях" вместо "целочисленных константных выражений", а затем иногда требуется не просто целочисленное константное выражение, но целочисленное константное выражение целочисленного типа, исключая char, wchar_t, а также bool (а может и signed char а также unsigned char? мне не понятно из текста).

Теперь, определение C99 константы нулевого указателя - вот о чем этот вопрос, поэтому я повторю это: 6.3.2.3p3 говорит

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

Стандарт очень, очень буквальный. Эти два предложения означают одно и то же:

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

(Курсив - определение термина. Жирный шрифт - мой акцент.) Так что это означает, в C, (long *)0 а также (long *)(void *)0 два способа написания абсолютно одного и того же, а именно нулевого указателя с типом long *,

С ++ отличается. Эквивалентный текст: C++98 4.10 [conv.ptr]:

Константа нулевого указателя - это целочисленное константное выражение (5.19) r целого типа, которое оценивается как ноль.

Это все. "Целочисленное константное выражение rvalue целочисленного типа" - это почти то же самое, что и "целочисленное константное выражение" в C99, но есть несколько вещей, которые соответствуют C, но не C++: например, в C символьный литерал '\x00' является выражением целочисленной константы и, следовательно, константой нулевого указателя, но в C++ это не является выражением целочисленной константы целочисленного типа, поэтому она также не является константой нулевого указателя.

Более того, C++ не имеет "или такое выражение, приведенное к void *"пункт. Это означает, что ((void *)0) не является константой нулевого указателя в C++. Это все еще нулевой указатель, но он не совместим с любым другим типом указателя. Это согласуется с общепринятой системой типов C++.

C++ 11 (но не AFAIK, C11) пересмотрел концепцию "нулевого указателя", добавив для них специальный тип (nullptr_t) и новое ключевое слово, которое оценивается как константа нулевого указателя (nullptr). Я не до конца понимаю изменения и не буду пытаться их объяснить, но я уверен, что 0 по-прежнему является действительной константой нулевого указателя в C++11.

Оценивая выражение (int*)0 дает нулевой указатель типа int*,

(int*)0 не является константой нулевого указателя.

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

C и C++ (будучи двумя разными языками) имеют немного разные правила в этой области. C++ не имеет "или такое выражение приведенное к типу void*Формулировка. Но я не думаю, что это влияет на ответ на ваш вопрос.

Что касается вашего вопроса о (long long)123Я не уверен, как это связано, но выражение 123 имеет тип intи приведение определяет преобразование из int в long long,

Я думаю, что основная путаница заключается в предположении, что актеры в (int*)0 не указывает конверсию, так как 0 уже является константой нулевого указателя. Но константа нулевого указателя не обязательно является выражением типа указателя. В частности, выражение 0 является константой нулевого указателя и выражением типа int; это не любой тип указателя. Термин константа нулевого указателя должен рассматриваться как единое понятие, а не фраза, значение которой зависит от отдельных слов, составляющих ее.

Другие вопросы по тегам