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

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

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

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

#define NULL 0

Или же

#define NULL (void *)0

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

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

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

11 ответов

Решение

Примечание. Этот ответ относится к языку 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 в FAQ по comp.lang.c. Смотрите этот PDF для стандарта C. Ознакомьтесь с разделами 6.3.2.3. Указатели, параграф 3.

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

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

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

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

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

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

  • контекст указателя - используется 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

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

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

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

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

#define NUL '\0'

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

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

"нулевой символ (NUL)" проще всего исключить. '\0' это буквальный символ. В Си это реализовано как 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++.

Одна хорошая вещь, которая помогает мне, когда я начинаю с 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". Два термина не являются взаимозаменяемыми по смыслу.

A-L NUL, он заканчивается строкой.

Нулевое значение L-L указывает ни на что.

И я поставлю золотой бык

Это не три-L NULLL.

Как вы справляетесь с NUL?

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

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

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

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

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

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

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

Другие вопросы по тегам