Подтвердить что ты не робот

Is ((void *) 0) константа нулевого указателя?

Я читаю это сообщение в блоге и в разделе Нулевые указательные константы и выраженные в скобках выражения ссылки на автора § 6.3.2.3 и § 6.5.1 из стандарта ISO C и говорит:

Он не говорит, что константа нулевого указателя в скобках указана в константе нулевого указателя.

Это означает, что, строго говоря, (void*)0 является нулевым указателем константа, но ((void*)0) не является.

Тогда:

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

В двух упомянутых разделах говорится:

§ 6.3.2.3

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

§ 6.5.1

Выражение в скобках является основным выражением. Тип и значение идентичны выражениям безпредметного выражения. Это lvalue, обозначение функции или выражение void, если unparenthesized выражение, соответственно, lvalue, функция указатель или выражение void.

Не противоречит ли смещенное предложение автору утверждения, что ((void*)0) не является константой нулевого указателя?

4b9b3361

Ответ 1

Не противоречит ли смещенное предложение автору утверждать, что ((void*)0) не является константой нулевого указателя?

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

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

Рассмотрим:

void *var = 0;

(void*)0 - константа нулевого указателя. ((void*)0) имеет тот же тип и значение, что и (void*)0. var также имеет тот же тип и значение, что и (void*)0, но var явно не является константой нулевого указателя.

Сказав это, я на 99% уверен, что намерение состоит в том, что ((void*)0) является константой нулевого указателя, и, как правило, любая константа нулевого указателя в скобках является константой нулевого указателя. Авторы этого стандарта просто не обращали на это внимания. А так как описание выражений в скобках в 6.5.1p5 специально перечисляет несколько других характеристик, которые наследуются выраженными в скобках выражениями:

Выражение в скобках является основным выражением. Его тип и стоимость идентичны выражениям несферообразного выражения. Это lvalue, обозначение функции или выражение void, если unparenthesized выражение, соответственно, lvalue, функция указатель или выражение void.

упущение вызывает тревогу (но только мягко).

Но допустим, ради аргумента, что ((void*)0) не является константой нулевого указателя. Какая разница?

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

6.5.9 Операторы равенства

Выражение типа указателя функции можно сравнить для равенства с константой нулевого указателя. (Указатель объекта можно сравнить с выражением типа void*, но указатель функции не может, если только он не является константой нулевого указателя.) Итак, это:

void func(void);
if (func == ((void*)0)) { /* ... */ }

будет нарушением ограничения.

6.5.16.1 Простое назначение

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

void (*fp)(void) = ((void*)0);

будет нарушением ограничения, если ((void*)0) не является константой нулевого указателя. Спасибо комментатору hvd за это.

7.19 Общие определения <stddef.h>

Макрос NULL расширяется до "константы нулевого указателя, определяемой реализацией". Если ((void*)0) не является константой нулевого указателя, то это:

#define NULL ((void*)0)

будет недействительным. Это будет ограничение, налагаемое на реализацию, а не на программистов. Обратите внимание, что это:

#define NULL (void*)0

определенно недействителен, поскольку макроопределения в стандартных заголовках должны быть полностью защищены скобками, где это необходимо (7.1.2p5). Без круглых скобок допустимое выражение sizeof NULL будет синтаксической ошибкой, расширяющейся до sizeof (void*), за которой следует посторонняя константа 0.

Ответ 2

Это выражение в скобках, которое содержит пустой указатель постоянная, так что неоспоримо является нулевым значением указателя. Использование его как rvalue имеет тот же эффект, что и использование "совместимой" версии в качестве r-значения.

Если бы существовали некоторые синтаксические правила, которые могли принимать только константу нулевого указателя, это не было бы квалифицировано. Но я не знаю ни одного (хотя я менее разбираюсь в C).

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

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

Имейте в виду, что эти правила в С++ сильно различаются, причем оба вышеуказанных выражения являются значениями константы нулевого указателя типа void*, но не универсальными константами указателя null. Конечные константы указателя в С++ - это интегральные константные выражения, которые оцениваются до нуля. И void* неявно конвертируется в другие типы указателей.