В чем разница между NULL, '\ 0' и 0?

309

В C, как представляется, различия между различными значениями нуля - NULL, NULи 0.

Я знаю, что символ ASCII '0'оценивается как 48или 0x30.

NULLУказатель обычно определяется как:

#define NULL 0

Или

#define NULL (void *)0

Кроме того, есть NULперсонаж, '\0'который, кажется, оценивает 0также.

Есть ли случаи, когда эти три значения не могут быть равны?

Это также верно для 64-битных систем?

gnavi
источник
1
См. Stackoverflow.com/questions/176989/… для получения дополнительной информации о различиях между 0 и NULL.
Дэвид Родригес - dribeas
7
Идентификатор NULне существует в стандартном языке или библиотеке C (или в C ++, насколько я знаю). Нулевой символ иногда называется NUL, но это C или C ++, обычно он называется просто '\0'.
Кит Томпсон

Ответы:

351

Примечание. Этот ответ относится к языку C, а не к C ++.


Нулевые указатели

Целочисленный константный литерал 0имеет разные значения в зависимости от контекста, в котором он используется. Во всех случаях это все еще целочисленная константа со значением0 , это просто описано по-разному.

Если указатель сравнивается с константным литералом 0, то это проверка, чтобы увидеть, является ли указатель нулевым указателем. Это 0затем называется постоянной нулевой указатель. Стандарт C определяет, что 0приведение к типуvoid * является как нулевым указателем, так и константой нулевого указателя.

Кроме того, для удобства чтения макрос NULLпредоставляется в заголовочном файле stddef.h. В зависимости от вашего компилятора может быть возможно#undef NULL и переопределить его в нечто дурацкое.

Поэтому вот несколько допустимых способов проверить наличие нулевого указателя:

if (pointer == NULL)

NULLопределяется для сравнения равным нулевому указателю. Это реализация, определяемая тем, что является фактическим определением NULL, при условии, что это допустимая константа нулевого указателя.

if (pointer == 0)

0 это еще одно представление константы нулевого указателя.

if (!pointer)

Это ifутверждение неявно проверяет «не 0», поэтому мы обращаемся к значению «0».

Ниже приведены недействительные способы проверки нулевого указателя:

int mynull = 0;
<some code>
if (pointer == mynull)

Для компилятора это не проверка на нулевой указатель, а проверка на равенство двух переменных. Это может сработать, если mynull никогда не изменится в коде, а константа оптимизации компилятора сложит 0 в оператор if, но это не гарантируется, и компилятор должен выдать хотя бы одно диагностическое сообщение (предупреждение или ошибка) в соответствии со стандартом C.

Обратите внимание, что это пустой указатель на языке Си. Это не имеет значения в базовой архитектуре. Если в базовой архитектуре значение нулевого указателя определено как адрес 0xDEADBEEF, то компилятор должен разобраться в этом беспорядке.

Таким образом, даже в этой забавной архитектуре следующие способы все еще являются допустимыми способами проверки на нулевой указатель:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

Ниже приведены недействительные способы проверки нулевого указателя:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

так как они рассматриваются компилятором как обычные сравнения.

Нулевые символы

'\0'определяется как нулевой символ - это символ со всеми битами, установленными в ноль. Это не имеет ничего общего с указателями. Однако вы можете увидеть что-то похожее на этот код:

if (!*string_pointer)

проверяет, указывает ли указатель строки на нулевой символ

if (*string_pointer)

проверяет, указывает ли строковый указатель на ненулевой символ

Не путайте их с нулевыми указателями. Просто потому, что битовое представление одно и то же, и это допускает некоторые удобные перекрестные случаи, на самом деле это не одно и то же.

Кроме того, '\0'является (как и все символьные литералы) целочисленной константой, в данном случае со значением ноль. Таким образом, '\0'он полностью эквивалентен 0целочисленной константе без приукрашивания - единственное отличие заключается в том, что намерение, которое оно передает читателю-человеку («Я использую это как нулевой символ»).

Ссылки

См Вопрос 5.3 comp.lang.c FAQ для более. Смотрите этот PDF для стандарта C. Ознакомьтесь с разделами 6.3.2.3. Указатели, пункт 3.

Эндрю Китон
источник
3
Спасибо за указание на список часто задаваемых вопросов. Однако см. Также c-faq.com/null/nullor0.html
Синан Юнюр
4
Нет, вы не сравнитесь ptrс нулевыми битами . Это не так memcmp, но это сравнение с использованием встроенного оператора. Одна сторона является константой нулевого указателя '\0', а другая сторона является указателем. Как и с двумя другими версиями с NULLи 0. Эти трое делают одно и то же.
Йоханнес Шауб -
6
Вы берете встроенный оператор сравнения как вещь, которая сравнивает битовые строки. Но это не то, что есть. Он сравнивает два значения, которые являются абстрактными понятиями. Таким образом , указатель нуля , что внутренне представляются как 0xDEADBEEFпо - прежнему является указатель NULL, независимо от того , что нравится его битовый внешний вид, и он будет по- прежнему считается равным NULL, 0, \0и все другими постоянными формами указателя нуля.
Йоханнес Шауб -
2
Вы делаете хорошее замечание об операторе сравнения. Я почистил С99. В нем говорится: «Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя». Это также говорит о том, что символьный литерал является целочисленным константным выражением. Таким образом, по переходному свойству вы правы в этом ptr == '\0'.
Эндрю Китон
2
«.... может быть возможно #undef NULL и переопределить его к чему-то дурацкому. Любой, кто делает это, заслуживает того, чтобы его застрелили». это мой хороший сэр заставил меня смеяться вслух ...
oggiemc
34

Похоже, что некоторые люди неправильно понимают разницу между NULL, '\ 0' и 0. Итак, чтобы объяснить, и в попытке избежать повторения сказанного ранее:

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

NULLмакрос, определенный как константа нулевого указателя .

\0это конструкция, используемая для представления нулевого символа , используемой для завершения строки.

Нулевой символ является байт , который имеет все биты , установленные в 0.

Аматэрасу
источник
14

Все три определяют значение нуля в разных контекстах.

  • контекст указателя - используется значение NULL и означает, что значение указателя равно 0, независимо от того, является ли оно 32-разрядным или 64-разрядным (один случай 4 байта, остальные 8 байтов нулей).
  • строковый контекст - символ, представляющий цифру ноль, имеет шестнадцатеричное значение 0x30, тогда как символ NUL имеет шестнадцатеричное значение 0x00 (используется для завершения строк).

Эти три всегда разные, когда вы смотрите на память:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20

Я надеюсь, что это проясняет это.

Наско
источник
8
Наско: Оценивай sizeof('\0')и удивляйся.
Кафе
3
@Nasko: Я был действительно удивлен: с gcc, в C: sizeof ('\ 0') == sizeof ('a') == 4, в то время как с g ++, в C ++: sizeof ('\ 0') == sizeof ('a') == 1
Дэвид Родригес - dribeas
1
@Nasko: Из стандарта C (черновик, n1124): «Целочисленная символьная константа имеет тип int», поэтому «\ 0» на самом деле имеет тип int в C, и, таким образом, sizeof («\ 0») равен 4 в моей архитектуре. (linux, 32bit)
Дэвид Родригес - dribeas
@ Dribeas - я не описывал это как константу, скорее то, что вы увидите как часть строки. Я определенно мог бы сделать это явным. Спасибо
Наско
@ DavidRodríguez-dribeas Undid edit "Исправлено значение 0 в ASCII до 0x20 (декабрь 32)"
chux - Восстановить Монику
6

Если NULL и 0 эквивалентны как константы нулевого указателя, что я должен использовать? в списке часто задаваемых вопросов C также решается эта проблема:

Программисты должны понимать , что NULLи 0являются взаимозаменяемыми в стрелочных контекстах, и что uncast 0 вполне приемлемо. Любое использование NULL (в отличие от 0) должно рассматриваться как мягкое напоминание о том, что указатель задействован; программисты не должны зависеть от этого (ни для собственного понимания, ни для компилятора), чтобы отличать указатели 0от целых 0.

Только в контекстах указателя NULLи 0эквивалентны. NULLне должен использоваться, когда 0требуется другой вид , даже если он может работать, потому что это посылает неправильное стилистическое сообщение. (Кроме того, ANSI допускает определение NULLbe ((void *)0), которое вообще не будет работать в контекстах без указателей.) В частности, не используйте, NULLкогда NULтребуется нулевой символ ASCII ( ). Укажите свое собственное определение

#define NUL '\0'

если вы должны.

Синан Юнюр
источник
5

В чем разница между NULL, '\ 0' и 0

"нулевой символ (NUL)" проще всего исключить. '\0'это буквальный символ. В C он реализован intтак, как и 0, что соответствует INT_TYPE_SIZE. В C ++ символьный литерал реализован как char, то есть 1 байт. Это обычно отличается от NULLили0 .

Следующий, NULL указывается значение указателя, которое указывает, что переменная не указывает на какое-либо адресное пространство. Если оставить в стороне тот факт, что он обычно реализован в виде нулей, он должен быть в состоянии выразить полное адресное пространство архитектуры. Таким образом, в 32-разрядной архитектуре NULL (вероятно) является 4-байтовым, а в 64-разрядной архитектуре - 8-байтовым. Это до реализации С.

Наконец, литерал 0имеет тип int, который имеет размер INT_TYPE_SIZE. Значение по умолчаниюINT_TYPE_SIZE может отличаться в зависимости от архитектуры.

Apple написал:

64-разрядная модель данных, используемая в Mac OS X, называется «LP64». Это общая модель данных, используемая другими 64-битными системами UNIX от Sun и SGI, а также 64-битной Linux. Модель данных LP64 определяет типы примитивов следующим образом:

  • 32-битные
  • длинные 64-битные
  • длинные-длинные также 64-битные
  • указатели 64-битные

Википедия 64-битная :

Компилятор Microsoft VC ++ использует модель LLP64.

64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

редактировать : Добавлено больше на литерале персонажа.

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

Приведенный выше код возвращает 4 на gcc и 1 на g ++.

Евгений Йокота
источник
2
Нет, '\0'это не 1-байтовое значение. Это символьный литерал, который является целочисленным константным выражением, поэтому если можно сказать, что он имеет размер, то это размер int(который должен быть не менее 2 байтов). Если вы мне не верите, оцените sizeof('\0')и убедитесь сами. '\0', 0И 0x0все полностью эквивалентны.
Кафе
@ Caf это зависит от языка. Если вы мне не верите, попробуйте sizeof('\0')компилятор C ++.
Юджин Йокота
2
вы должны использовать "% zu" при печати sizeof (что-то)
Unused
4

Одна хорошая вещь, которая помогает мне, когда я начинаю с C (взято из Expert C Programming от Linden)

Один 'l' nul и два 'l' null

Запомните этот небольшой стих, чтобы вспомнить правильную терминологию для указателей и нулевого ASCII:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 

Символ ASCII с битовой комбинацией нуля называется «NUL». Специальное значение указателя, которое означает, что указатель нигде не является «NULL». Эти два термина не являются взаимозаменяемыми по смыслу.

dlmeetei
источник
Намного проще: NULуправляющий код , такой как BEL, VT, HT, и SOTт.д. , и , таким образом , имеет макс. 3 персонажа.
glglgl
2

«NUL» не равно 0, но относится к символу ASCII NUL. По крайней мере, так я это видел. Нулевой указатель часто определяется как 0, но это зависит от среды, в которой вы работаете, и спецификации используемой операционной системы или языка.

В ANSI C пустой указатель указывается как целочисленное значение 0. Так что любой мир, где это не так, не соответствует ANSI C.

Петерб
источник
1

В 0x00таблице ASCII байт со значением - это специальный символ, называемый NULили NULL. В C, поскольку вы не должны встраивать управляющие символы в исходный код, это представляется в строках C с экранированным 0, т \0. Е.

Но истинный NULL не является значением. Это отсутствие ценности. Для указателя это означает, что указателю не на что указывать. В базе данных это означает, что в поле нет значения (это не то же самое, что сказать, что поле пустое, 0 или заполнено пробелами).

Фактическое значение данной системы или формат файла базы данных используется для представления NULLне обязательно 0x00.

richardtallent
источник
0

NULLне гарантируется равным 0 - его точное значение зависит от архитектуры. Большинство основных архитектур определяют это (void*)0.

'\0' всегда будет равняться 0, потому что именно так байт 0 кодируется в символьном литерале.

Я не помню, требуется ли компиляторам C использовать ASCII - если нет, то '0'может не всегда равняться 48. Несмотря на это, вряд ли вы когда-нибудь столкнетесь с системой, которая использует альтернативный набор символов, такой как EBCDIC, если вы не работаете над очень неясные системы.

Размеры различных типов будут отличаться в 64-битных системах, но целочисленные значения будут одинаковыми.


Некоторые комментаторы выражают сомнение в том, что NULL будет равно 0, но не будет нулевым. Вот пример программы вместе с ожидаемым выходом в такой системе:

#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

Эта программа может напечатать:

NULL == 0
NULL = 0x00000001
Джон Милликин
источник
2
ОП спрашивал о «\ 0» (символ NUL), а не «0» (нулевой символ)
Крис Латс
2
@Chris: '\ 0' не NULL, это байт 0, закодированный в восьмеричном виде в символьном литерале.
Джон Милликин
2
В C ++ стандарт гарантирует, что преобразование целочисленного значения 0 в указатель всегда будет давать нулевой указатель. В C ++ 0 гарантированно является нулевым указателем, а с другой стороны, NULL является макросом, и злонамеренный кодер может переопределить его как нечто иное.
Дэвид Родригес - dribeas
6
И NULL гарантированно будет 0. Битовый шаблон указателя NULL не обязательно будет иметь все нули, но константа NULL равна и всегда будет 0.
jalf
2
Ваше первое предложение неверно - NULL не может быть определен как (void *) 0 в C ++, потому что не существует неявного преобразования из void * в другой указатель (в отличие от C).
-2

(void *) 0 равно NULL, а '\ 0' представляет конец строки.

shinxg
источник