Я неопределенно не забываю читать об этом несколько лет назад, но я не могу найти ссылку в сети.
Можно ли дать мне пример, где ПУСТОЙ макрос не расширился до 0?
Редактирование для ясности: Сегодня это расширяется до также ((void *)0)
, (0)
, или (0L)
. Однако была архитектура, давно забытая, где это не было верно, и ПУСТОЙ УКАЗАТЕЛЬ, расширенный до другого адреса. Что-то как
#ifdef UNIVAC
#define NULL (0xffff)
#endif
Я ищу пример такой машины.
Обновление для решения проблем:
Я не имел в виду этот вопрос в контексте текущих стандартов, или расстраивать людей с моей неправильной терминологией. Однако мои предположения были подтверждены принятым ответом:
Более поздние модели используются [вздор], очевидно как кусок ко всему существующему плохо записанному коду C который сделанные неправильными предположениями.
Для дискуссии о нулевых указателях в текущем стандарте посмотрите этот вопрос.
В FAQ по C есть несколько примеров исторических машин с представлениями, отличными от 0 NULL.
Из Списка часто задаваемых вопросов по C , вопрос 5.17 :
В: Серьезно, действительно ли какие-либо машины действительно использовали ненулевые нулевые указатели или другие представления для указатели на разные типы ?
A: Серия Prime 50 использовала сегмент 07777, смещение 0 для нулевого указателя , по крайней мере, для PL / I. В более поздних моделях для нулевых указателей в C использовался сегмент 0, смещение 0, что требовало новых инструкций, таких как TCNP (Test C Null Pointer), очевидно, в качестве подачки [сноски] ко всем существующим {{ 1}} плохо написанный код C, делающий неверные предположения. Старые машины Prime с адресом слов также были известны тем, что требовали больших байтовых указателей (
char *
), чем указатели слов (int *
' с).Серия Eclipse MV от Data General имеет три архитектурно поддерживаемых формата указателя (словесные, байтовые и битовые указатели), два из которых используются компиляторами C: байтовые указатели для
char *
иvoid *
, а также указатели word для всего остального. По историческим причинам во время эволюции 32-битной строки MV из 16-битной строки Nova указатели слов и байтовые указатели имели защиту смещения, косвенности и кольца биты в разных местах в слове. Передача несоответствующего формата указателя в функцию приводила к ошибкам защиты. В конце концов, компилятор MV C добавил много параметров совместимости, чтобы попытаться справиться с кодом, в котором были ошибки несоответствия типа указателя .Некоторые мэйнфреймы Honeywell-Bull используют битовый шаблон 06000 для (внутренних) нулевых указателей.
CDC Cyber 180 Series имеет 48-битные указатели, состоящие из кольца, сегмента и смещения. Большинство пользователей (в кольце 11) имеют нулевые указатели 0xB00000000000. На старых машинах CDC с дополнением до единицы было обычным делом использовать слово, состоящее из одного бита, в качестве специального флага для всех видов данных, включая недопустимые адреса.
Старая серия HP 3000 использует другую схему адресации для байтовых адресов, чем для словарных адресов; как и некоторые из машин выше , поэтому он использует разные представления для указателей
char *
иvoid *
, чем для других указателей.Symbolics Lisp Machine, архитектура с тегами, даже не имеет обычных числовых указателей; он использует пару
(в основном, дескриптор несуществующий
<объект, смещение>
) в качестве нулевого указателя C.В зависимости от используемой «модели памяти» процессоры семейства 8086 (совместимые с ПК ) могут использовать 16-разрядные указатели данных и 32-разрядные указатели функций или наоборот.
Некоторые 64-битные машины Cray представляют
int *
в младших 48 битах слова ;char *
дополнительно использует некоторые из старших 16 битов для указания байтового адреса в слове.
Давным-давно это было напечатано как ((void *) 0)
или какой-либо другой машинно-зависимый способ, где эта машина не использовала битовый шаблон «все нули».
Некоторые платформы (некоторые машины CDC или Honeywell) имели другой битовый шаблон для NULL (т. Е. , а не все нули), хотя ISO / ANSI исправил это до ратификации C90, указав, что 0
был правильным указателем NULL в исходном коде, независимо от базовой битовой комбинации. Из C11 6.3.2.3 Pointers / 4
(хотя, как уже упоминалось, эта формулировка восходит к C90):
Целочисленное постоянное выражение со значением
0
, или такое выражение, приведенное к типуvoid *
, называется константой нулевого указателя.
NULL
макрос в C расширяется до константы нулевого указателя, определенной реализацией. Это может быть что угодно (поскольку это определяется реализацией), но в контексте указателя эффект всегда такой же, как если бы он был расширен до константы 0
.
В стандартной истории C никогда не было случая, когда NULL
расширялся до чего-то конкретно не 0
, если только вы не рассматриваете (void *) 0
как " не 0 ". Но (void *) 0
для NULL
широко используется и по сей день.
В файле GNU libio.h:
#ifndef NULL
# if defined __GNUG__ && \
(__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
# define NULL (__null)
# else
# if !defined(__cplusplus)
# define NULL ((void*)0)
# else
# define NULL (0)
# endif
# endif
#endif
Обратите внимание на условную компиляцию в __cplusplus. C ++ не может использовать ((void *) 0) из-за более строгих правил приведения типов указателей; стандарт требует, чтобы NULL было 0. C допускает другие определения NULL.
В компиляторах C он может расширяться до ' ((void *) 0)
' (но не обязательно) . Это не работает для компиляторов C ++.
См. Также C FAQ, в котором есть целая глава о нулевых указателях .
В современном C void * pointer = 0;
предназначен для инициализации «указателя», чтобы он ни на что не указывал. Это зависит от платформы относительно того, достигается ли это установкой битов «указателя» на все нули.
В прошлом это формальное значение «0» в контексте указателя не было установлено. Необходимо было установить указатель на фактическое значение, которое платформа трактовала как «никуда не указывает». Например, платформа может выбрать какой-то фиксированный адрес, которому никогда не сопоставляется страница. В этом случае в старом компиляторе платформа могла бы определить NULL
как:
#define NULL ((void*)0xFFFFF000)
Конечно, сегодня нет причин не определять его как ((void *) 0)
.