Страницы

Поиск по вопросам

среда, 3 октября 2018 г.

Обязательно ли NULL в Си является указателем?

Вопрос на основе обсуждения ответа про sizeof NULL
Вот куски стандарта:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant NULL which expands to an implementation-defined null pointer constant
Кажется, что не запрещено вместо #define NULL ((void*)0) сделать просто #define NULL 0, как это сделано в плюсах. Тогда получается, что sizeof NULL равен либо sizeof (void*), либо sizeof (int) (в варианте sizeof 0).
Однако, @Vlad from Moscow утверждает, что NULL в Си обязан быть указателем.
NULL в С определен как указатель. Ключевое значение в приведенной цитате имеют слова "cast to type void *". Здесь или имеет отношение не к приведению типов, а к виду выражения. И именно таким образом определяется NULL.
Так допустимо ли для Си (без плюсов) иметь?
#define NULL 0
И если допустимо, то почему в компиляторах используется
#ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif
вместо более простого варианта с нулём для обоих языков? Ведь в си даже перегрузки функций не было, чтобы можно было что-то сломать.


Ответ

Просмотрев внимательно стандарт C, я думаю, что вы правы. null pointer constant - это либо целочисленное константное выражение со значением 0, либо такое выражение, приведенное к типу void *
Поэтому определение макроса NULL может быть в принципе различным, зависящим от реализации компилятора. То есть нигде в стандарте C я не нашел, что макрос NULL обязан быть определен как
( void *)0
Я заглянул в документ Rationale for International Standard— Programming Languages— C и там нашел следующее (7.17 Common definitions )
25 NULL can be defined as any null pointer constant. Thus existing code can retain definitions of NULL as 0 or 0L, but an implementation may also choose to define it as (void*)0. This latter form of definition is convenient on architectures where sizeof(void*) does not equal the size of any integer type.
Так как null pointer constant преобразуется в выражениях в null pointer, а размер null pointer может быть не равен размеру ни одному целочисленному типу, то удобнее определять null pointer constant как
( void * )0
то есть сразу же приводить ее к типу указателя.
В C++ от такого определения отказались, так как в отличии от C в C++ в связи с обеспечением безопасности типов нужно явное приведение указателя на void к типу указателя на конкретный объект. То есть если null pointer constant NULL будет определена в C++ как
( void *)0
то вам придется делать явное приведение указателей, как, например,
int *p = ( int * )( void *)0;
что, естественно, очень обременительно.
Поэтому в C++ Отказались объявлять NULL в виде целочисленной константы, приведенной к типу void *

Комментариев нет:

Отправить комментарий