Когда был NULL макрос не 0?
Я смутно помню, как читал об этом пару лет назад, но я не могу найти ссылку в сети.
Можете ли вы привести мне пример, когда макрос NULL не расширялся до 0?
Изменить для ясности: Сегодня он расширяется до ((void *)0)
, (0)
или (0L)
. Однако были давно забыты архитектуры, где это было неверно, и NULL расширился до другого адреса. Что-то вроде
- Как определить строковый литерал в командной строке gcc?
- Последовательные sys_write syscalls не работают должным образом, ошибка NASM на OS X?
- Как работает макрос смещения C?
- Создание макроса C с ## и __LINE__ (конкатенация маркера с помощью макроса позиционирования)
- Является ли #pragma когда-то частью стандарта C ++ 11?
#ifdef UNIVAC #define NULL (0xffff) #endif
Я ищу пример такой машины.
Обновите для решения проблем:
Я не имел в виду этот вопрос в контексте существующих стандартов, или чтобы расстроить людей своей неправильной терминологией. Однако мои предположения были подтверждены принятым ответом:
Более поздние модели использовали [blah], очевидно, как сосок для всего сохранившегося плохо написанного кода на C, который делал неправильные предположения.
Для обсуждения нулевых указателей в текущем стандарте см. Этот вопрос .
- Как реализовать макрос (или шаблон) no-op в C ++?
- Массив размера массива, который отклоняет указатели
- Swift: как использовать флаги PREPROCESSOR (например, `#if DEBUG`) для реализации ключей API?
- Регистрация типа C ++ во время компиляции
- C Определение макроса для определения большой конечной или малоконечной машины?
- Являются ли шаблоны C ++ просто маскируемыми?
- Прочитайте файл / URL-адрес по строке в Swift
В C FAQ есть несколько примеров исторических машин с представлениями, отличными от 0 NULL.
Из списка часто задаваемых вопросов C , вопрос 5.17 :
В: Серьезно, действительно ли в реальных машинах используются ненулевые нулевые указатели или разные представления для указателей на разные типы?
A: В серии Prime 50 используется сегмент 07777, смещение 0 для нулевого указателя, по крайней мере для PL / I. В более поздних моделях используется сегмент 0, смещение 0 для нулевых указателей в C, что требует новых инструкций, таких как TCNP (Test C Null Pointer), очевидно, как sop для [footnote] всего существующего плохо написанного кода на C, который сделал неправильные предположения. Старые, ориентированные на слово Prime машины также были известны тем, что требовали более крупных указателей байтов (char *), чем указатели на слова (int *).
Серия Eclipse MV из Data General имеет три архитектурно поддерживаемых формата указателя (слова, байты и указатели на бит), два из которых используются компиляторами C: байтовые указатели для char * и void * и указатели слов для всего остального. По историческим причинам в ходе эволюции 32-битной линии MV из 16-разрядной линии Nova указатели на слова и указатели байтов имели биты смещения, косвенности и защиты кольца в разных местах слова. Передача несогласованного формата указателя в функцию привела к ошибкам защиты. В конце концов, компилятор MV C добавил много вариантов совместимости, чтобы попытаться разобраться с кодом, который имел ошибки несоответствия типа указателя.
Некоторые мейнфреймы Honeywell-Bull используют битовый шаблон 06000 для (внутренних) нулевых указателей.
Серия CDC Cyber 180 имеет 48-битные указатели, состоящие из кольца, сегмента и смещения. Большинство пользователей (в кольце 11) имеют нулевые указатели 0xB00000000000. Общеизвестно, что на старых CDC-устройствах с дополнительными машинами использовать однобитовое слово как специальный флаг для всех видов данных, включая недействительные адреса.
В старых сериях HP 3000 используется другая схема адресации для адресов байтов, чем для адресов слов; как и некоторые из вышеперечисленных машин, поэтому использует разные представления для указателей char * и void *, чем для других указателей.
Symbolics Lisp Machine, tagsрованная архитектура, даже не имеет обычных числовых указателей; он использует пару (в основном несуществующий дескриптор) как С-нулевой указатель.
В зависимости от используемой модели «памяти» процессоры семейства 8086 (совместимые с ПК) могут использовать 16-разрядные указатели данных и 32-разрядные указатели функций или наоборот.
Некоторые 64-битные машины Cray представляют int * в младших 48 бит слова; char * дополнительно использует некоторые из верхних 16 бит для указания байтового адреса внутри слова.
В компиляторах C он может расширяться до ‘ ((void *)0)
‘ (но не обязательно). Это не работает для компиляторов C ++.
См. Также FAQ по C, в котором содержится целая глава о нулевых указателях .
В файле 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.
Было некоторое время назад, когда он был напечатан как ((void*)0)
или какой-то другой машинный характер, когда эта машина не использовала шаблон с нулевым битом.
Некоторые платформы (некоторые устройства CDC или Honeywell) имели различный битовый шаблон для NULL (т. Е. Не все нули), хотя ISO / ANSI фиксировали, что до того, как C90 был ратифицирован, указав, что 0
был правильным указателем NULL в исходном коде, независимо от базовый бит-шаблон. Из C11 6.3.2.3 Pointers /4
(хотя, как уже упоминалось, эта формулировка полностью возвращается к C90):
Целочисленное константное выражение со значением
0
или такое выражение, отлитое от типаvoid *
, называется константой нулевого указателя.
В современном C, void *pointer = 0;
предназначен для инициализации «указателя», чтобы не указывать ни на что. Это зависит от платформы, независимо от того, выполняется ли это путем установки битов «указателя» на все ноль.
Раньше это формальное значение «0» в контексте указателя не было установлено. Необходимо было установить указатель на фактическое значение, которое платформа обрабатывала как «не указывает нигде». В качестве примера платформа может выбрать какой-либо фиксированный адрес, который никогда не будет отображать на него страницу. В этом случае в старом компиляторе платформа может определить NULL
как:
#define NULL ((void*)0xFFFFF000)
Конечно, сегодня нет причин не определять его как ((void*)0)
.
Компиляторы C обычно используют ((void *)0)
. Причиной является передача NULL
в функции с переменными аргументами (или теперь редкие, но все же правовые функции без прототипа). Когда указатели больше, чем int, 0
будет продвигаться только до int
и, следовательно, не будет корректно считываться в качестве указателя.
Компиляторы C ++ не могут использовать это определение, потому что C ++ не допускает неявного литья из void *
(отлитие 0
от любого указателя происходит с особым обложением). Однако C ++ 11 ввел новое ключевое слово nullptr
которое является константой нулевого указателя специального типа nullptr_t
неявно конвертируемой в любой тип указателя, но не числа. Это решает как проблему вариационного аргумента, так и неявные литые и дополнительно более серьезные проблемы с выбором перегрузки ( 0
по очевидной причине, выбирает перегрузку int
over over pointer one). Легально определять их сами для более старых компиляторов, и некоторые компиляторы C ++ пытались это сделать в прошлом.
Макрос NULL
в C расширяется до заданной константы нулевого указателя. Это может быть что угодно (поскольку оно определено реализацией), но в контексте указателя эффект всегда такой же, как если бы он был расширен до константы 0
.
В стандартной истории C никогда не было времени, когда NULL
расширилось до чего-то конкретно не 0
, если вы не считаете (void *) 0
«не 0». Но (void *) 0
для NULL
широко используется и по сей день.