Является ли ((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 или такое выражение, приведенное к типу void *, называется константой нулевого указателя.
§ 6.5.1
Заключенное в скобки выражение является основным выражением. Его тип и значение идентичны типам выражения без скобок. Это lvalue, обозначение функции или выражение void, если выражение без скобок является, соответственно, lvalue, указателем функции или выражением void.
Разве это предложение не противоречит утверждению автора о том, что ((void*)0)
не является константой нулевого указателя?
2 ответа
Разве это предложение не противоречит утверждению автора о том, что
((void*)0)
не является константой нулевого указателя?
Нет, это не так. (Признаюсь, что я немного предвзят, так как упомянутый блог мой.)
Выделенное жирным шрифтом предложение говорит о том, что его тип и значение идентичны таковым для выражения без скобок. Этого недостаточно, чтобы подразумевать, что это константа нулевого указателя.
Рассматривать:
void *var = 0;
(void*)0
является константой нулевого указателя. ((void*)0)
имеет тот же тип и значение, что и (void*)0
, var
также имеет тот же тип и значение, что и (void*)0
, но var
явно не является константой нулевого указателя.
Сказав это, я уверен на 99%, что ((void*)0)
является константой нулевого указателя, и в более общем смысле любая константа нулевого указателя в скобках является константой нулевого указателя. Авторы стандарта просто забыли об этом сказать. А поскольку описание выражений в скобках в 6.5.1p5 специально перечисляет несколько других характеристик, которые наследуются выражениями в скобках:
Заключенное в скобки выражение является основным выражением. Его тип и значение идентичны типам выражения без скобок. Это lvalue, обозначение функции или выражение void, если выражение без скобок является, соответственно, 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)
не были константой нулевого указателя. Спасибо commenter hvd за то, что нашел это.
7.19 Общие определения <stddef.h>
Макрос NULL
расширяется до "определенной константой реализации нулевого указателя". Если ((void*)0)
не является константой нулевого указателя, тогда это:
#define NULL ((void*)0)
будет недействительным. Это было бы ограничением для реализации, а не для программистов. Обратите внимание, что это:
#define NULL (void*)0
определенно недействителен, так как определения макросов в стандартных заголовках должны быть полностью защищены скобками при необходимости (7.1.2p5). Без скобок, допустимое выражение sizeof NULL
будет синтаксическая ошибка, расширяющаяся до sizeof (void*)
сопровождаемый посторонней константой 0
,
Это выражение в скобках, которое содержит нулевую константу указателя, так что неоспоримо является нулевым значением указателя. Использование его в качестве значения r имеет тот же эффект, что и использование "совместимой" версии в качестве значения r.
Если бы были некоторые синтаксические правила, которые могли бы принимать только константу нулевого указателя, это не отвечало бы требованиям. Но я не знаю ни одного (хотя я менее эксперт по Си).
И хотя ни один из них не является константой (ссылаясь на создание формальной грамматики), оба могут появляться в выражении константы в инициализаторе, поскольку допустимы как константы нулевого указателя, так и константы адреса, а значение константы нулевого указателя явно включено в категорию адресной константы.
Сравнения указателей также специально упоминают константы нулевого указателя... но здесь значения указателя также принимаются, и все значения нулевого указателя обрабатываются одинаково. То же самое для троичных операторов и операторов присваивания.
Пожалуйста, имейте в виду, что эти правила сильно отличаются в C++, где оба приведенных выше выражения являются постоянными значениями нулевого указателя типа void*
, но не универсальные константы нулевого указателя. Константы нулевого указателя в C++ являются целочисленными константными выражениями, которые оцениваются как ноль. А также void*
неявно конвертируется в другие типы указателей.
Попробуйте напечатать ниже строку в вашем коде C:
Е ("% р",(недействительными *)0);
Вы получите вывод как:
(Ноль)