Как правильно написать код C / C ++, когда нулевой указатель не все биты ноль

Как comp.lang.c FAQ говорит, что есть архитектуры, где нулевой указатель не все биты ноль. Так что вопрос в том, что на самом деле проверяет следующую конструкцию:

void* p = get_some_pointer();
if (!p)
return;

Я сравниваю p с машинно-зависимым нулевым указателем или я сравниваю p с арифметическим нулем?

Должен ли я написать

void* p = get_some_pointer();
if (NULL == p)
return;

вместо этого быть готовым к такой архитектуре или это просто моя паранойя?

69

Решение

Согласно спецификации C:

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

Так 0 является константой нулевого указателя. И если мы преобразуем его в тип указателя, мы получим нулевой указатель, который может быть не-все-бит-ноль для некоторых архитектур. Далее давайте посмотрим, что спецификация говорит о сравнении указателей и константы нулевого указателя:

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

Давайте рассмотрим (p == 0): первый 0 преобразуется в нулевой указатель, а затем p сравнивается с константой нулевого указателя, чьи фактические значения битов зависят от архитектуры.

Далее, посмотрите, что спецификация говорит об операторе отрицания:

Результат оператора логического отрицания! 0, если значение его
операнд сравнивает неравный с 0, 1, если значение его операнда сравнивается
равно 0. Результат имеет тип int. Выражение! E эквивалентно
к (0 == E).

Это означает, что (!p) эквивалентно (p == 0) что, согласно спецификации, тестирование p против машинной константы нулевого указателя.

Таким образом, вы можете смело писать if (!p) даже на архитектурах, где константа нулевого указателя не равна нулю.

Что касается C ++, константа нулевого указателя определяется как:

Константа нулевого указателя является интегральным константным выражением (5.19)
prvalue целочисленного типа с нулевым значением или prvalue типа
станд :: nullptr_t. Константа нулевого указателя может быть преобразована в указатель
тип; результатом является значение нулевого указателя этого типа и
отличается от любого другого значения указателя объекта или функции
тип указателя

Что близко к тому, что мы имеем для C, плюс nullptr синтаксис сахар. Поведение оператора == определяется:

Кроме того, можно сравнивать указатели на элементы или указатель на
член и константа нулевого указателя. Указатель на членство преобразований
(4.11) и квалификационные преобразования (4.4) выполняются, чтобы привести их
к общему типу. Если один операнд является константой нулевого указателя,
общий тип — это тип другого операнда. В противном случае, общее
тип — указатель на тип члена, аналогичный (4.4) типу одного из
операнды с квалификационной подписью cv (4.4), которая является
объединение cv-квалификационных сигнатур типов операндов. [ Заметка:
это означает, что любой указатель на член можно сравнить с нулевым
константа указателя. — конец примечания]

Это приводит к преобразованию 0 к типу указателя (как для C). Для оператора отрицания:

Операнд оператора логического отрицания! контекстуально
преобразован в bool (пункт 4); его значение истинно, если преобразованный
операнд истинный и ложный в противном случае. Тип результата — bool.

Это означает, что результат !p зависит от того, как преобразование из указателя на bool выполняется. Стандарт гласит:

Нулевое значение, нулевое значение указателя или нулевое значение указателя элемента
преобразуется в ложь;

Так if (p==NULL) а также if (!p) делает то же самое и в C ++.

98

Другие решения

Неважно, является ли нулевой указатель нулем всех битов или нет на реальной машине. Если предположить, p это указатель:

if (!p)

всегда легальный способ проверить, p нулевой указатель, и он всегда эквивалентен:

if (p == NULL)

Вы можете быть заинтересованы в другой статье C-FAQ: Это странно. NULL гарантированно равен 0, но нулевой указатель — нет?


Выше верно как для C, так и для C ++. Обратите внимание, что в C ++ (11) предпочтительно использовать nullptr для литерала нулевого указателя.

32

Этот ответ относится к C.

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

От С11 6.3.2.3:

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

66) Макрос NULL определен в <stddef.h> (и другие заголовки) как константа нулевого указателя; см. 7.19.

7,19:

Макросы

НОЛЬ

которая расширяется до определенной в реализации постоянной нулевого указателя;

Реализация определяется в случае NULL, либо 0 или же (void*)0, NULL не может быть ничего другого.

Однако, когда указателю присваивается константа с нулевым указателем, вы получаете нулевой указатель, который может не иметь значения ноль, даже если он сравнивается равным константе нулевого указателя. Код if (!p) не имеет ничего общего с NULL макрос, вы сравниваете нулевой указатель с арифметическим значением ноль.

Так что в теории, код как int* p = NULL может привести к нулевому указателю p который отличается от нуля.

10

В свое время компьютеры STRATUS имели нулевые указатели как 1 на всех языках.

Это вызвало проблемы для C, поэтому их компилятор C позволил бы при сравнении указателей 0 и 1 возвращать true

Это позволило бы:

void * ptr=some_func();
if (!ptr)
{
return;
}

к return на нулевом ptr, хотя вы могли видеть, что ptr имел значение 1 в отладчике

if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}

Будет ли на самом деле печатать «Добро пожаловать в СТРАТУС»

7

Если ваш компилятор хорош, есть две вещи (и только две), на которые нужно обратить внимание.

1: статические по умолчанию инициализированные (то есть не назначенные) указатели не будут иметь NULL в них.

2: memset () для структуры или массива или расширения calloc () не будет устанавливать указатели в NULL.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector