Когда константа 0 в контексте указателя приобретает свой особый статус?
Как известно, в стандартном современном языке C значение константы 0, используемое в контексте указателя, действует как константа нулевого указателя, которая преобразуется в значение нулевой указателя на конкретную платформу (и, возможно, даже для конкретного типа).
Между тем, ранние версии языка C, как описано в Справочное руководство по C, не делали большой разницы между указателем и целочисленные контексты, позволяющие свободно сравнивать и присваивать целые числа указателям. Если не ошибаюсь, в этой версии C константа 0 не имела особого статуса, то есть назначение значения константы 0 указателю просто указывало бы на физический адрес 0 (точно так же, как присвоение значения 42 указателю было бы укажите его физический адрес 42).
В ANSI C вещи значительно изменились. Теперь присвоение константы 0 указателю поместит в этот указатель значение нулевой указателя для конкретной платформы. Значение Null-указателя не должно быть представлено физическим значением 0.
Итак, в какой момент в истории языка C это изменилось от одного к другому? Разве K & R C уже включил концепцию нулевого указателя более высокого уровня с константой 0, учитывая ее особый статус? Или K & R C по-прежнему гарантирует физическое назначение целых чисел указателям даже для константы 0?
Ответы
Ответ 1
Он восходит к началу C (если не самому началу). Если вы посмотрите на стр. 21 справочного руководства Январь 1974 года C), это более или менее прямо указано в некотором примерном коде:
/* is pointer null? */
if (p == 0) {
Возвращаясь еще немного, ca. 1972-73 компилятор PDP-11/20, мы находим:
match(tree, table, nreg)
int tree[], table[]; {
extern opdope[], dcalc, notcompat;
int op, d1, d2, t1, t2, p1[], p2[];
char mp[];
if (tree==0)
return(0);
op = *tree;
По крайней мере, если я правильно это читаю, строка if (tree==0)
проверяет, что tree
является ненулевым указателем, прежде чем пытаться разыменовать его.
К сожалению, Деннис говорит, он не может быть более уверен в дате, чем "1972-73".
До этого не так много истории C. Тем не менее, похоже, что некоторая история 0 обрабатывается как нулевой указатель. Мне кажется, что использование 0 в качестве нулевого указателя - это то, что C "унаследовано" от Unix. Запись для exec
в ноябрь 1971 1 st Редактор Unix для программистов Unix показывает указатель со значением 0, чтобы сигнализировать о завершении список аргументов. Согласно описанию Дениса, в этот момент "C еще должен был прийти".
Основываясь на этом, я бы предположительно сделал вывод, что C обрабатывал 0 как нулевой указатель с самого начала или, по крайней мере, так рано, что, вероятно, больше не было записей о версии языка, которая была в противном случае.
Я не был так успешным при поиске документации о первой точке, где нулевой указатель мог иметь ненулевые биты. С точки зрения языка это никогда не было актуальным. Я подозреваю, что это произошло довольно рано, но найти документацию для поддержки было бы сложно. Один из самых ранних портов C был IBM System/360 мейнфреймов, и хотя я не могу найти прямую документацию по нему, я бы предположил, что внутренне нулевой указатель значение, используемое на этих машинах, было, вероятно, отличным от нуля. У меня нет точного номера, но я знаю, что PL/I на этих машинах использовал ненулевое значение для своего эквивалента нулевого указателя; Я предполагаю, что когда они портировали C на эти машины, они, вероятно, использовали одно и то же значение.
Ответ 2
См. вопрос C-faq 5.4
В стиле, многие программисты предпочитают не иметь unadorned 0, разбросанных по их программам, некоторые представляют числа и некоторые представляющие указатели. Следовательно, макрос препроцессора NULL определяется (несколькими заголовками, включая и) в качестве константы нулевого указателя, обычно 0 или ((void *) 0) (см. Также вопрос 5.6). Программист, который хочет сделать явным различие между 0 целым числом и нулевой константой указателя, может использовать NULL всякий раз, когда требуется нулевой указатель.
Использование NULL - это только стилистическая конвенция; препроцессор возвращает NULL обратно в 0, который затем распознается компилятором в контекстах указателей, как и раньше. В частности, бросок может быть необходим до NULL (как и до 0) в аргументе вызова функции. Таблица под вопросом 5.2 выше применяется для NULL, а также для 0 (незакрашенный NULL эквивалентен незакрашенному 0).
NULL следует использовать только как константу указателя; см. вопрос 5.9.
References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
ISO Sec. 7.1.6, Sec. 6.2.2.3
Rationale Sec. 4.1.5
H&S Sec. 5.3.2 p. 122, Sec. 11.1 p. 292
Что это за позорный нулевой указатель?
В определении языка указано, что для каждого типа указателя существует специальное значение - "нулевой указатель" - которое отличается от всех других значений указателя и которое "гарантировано сравнивается неравномерно с указателем на любой объект или функция". То есть нулевой указатель указывает окончательно нигде; это не адрес какого-либо объекта или функции. Адрес-оператора и никогда не даст нулевого указателя и не будет успешным вызовом malloc. [Footnote] (malloc возвращает пустой указатель, когда он терпит неудачу, и это типичное использование нулевых указателей: как "специальный", значение указателя с каким-либо другим значением, обычно "не выделено" или "пока еще не указывается".)
Нулевой указатель концептуально отличается от неинициализированного указателя. Известно, что нулевой указатель не указывает на какой-либо объект или функцию; неинициализированный указатель может указывать в любом месте. См. Также вопросы 1.30, 7.1 и 7.31.
Как упоминалось выше, для каждого типа указателя имеется нулевой указатель, а внутренние значения нулевых указателей для разных типов могут быть разными. Хотя программистам не обязательно знать внутренние значения, компилятор всегда должен знать, какой тип нулевого указателя требуется, чтобы он мог провести различие в случае необходимости (см. Вопросы 5.2, 5.5 и 5.6).
References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
ISO Sec. 6.2.2.3
Rationale Sec. 3.2.2.3
H&S Sec. 5.3.2 pp. 121-3
Наконец, только константные интегральные выражения со значением 0 гарантируют указание нулевых указателей.