Ответ 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
.