Яка різниця між NULL, '\ 0' та 0?


309

У C, як видається, відмінності між різними значеннями нуля - NULL, NULі 0.

Я знаю, що символ ASCII '0'оцінює до 48або 0x30.

NULLПокажчик зазвичай визначається як:

#define NULL 0

Або

#define NULL (void *)0

Крім того, є NULхарактер, '\0'який, здається, також оцінює 0.

Чи бувають випадки, коли ці три значення не можуть бути рівними?

Це справедливо і для 64-бітних систем?


1
Дивіться stackoverflow.com/questions/176989/… для отримання додаткової інформації про відмінності між 0 та NULL.
Девід Родрігес - дрибес

7
Ідентифікатор NULне існує в стандартній мові або бібліотеці С (або на 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".

Нижче наведено INVALID способи перевірки нульового вказівника:

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

Для компілятора це не перевірка нульового вказівника, а перевірка рівності двох змінних. Це може спрацювати, якщо mynull ніколи не змінює код і константи оптимізації компілятора, складіть 0 на оператор if, але це не гарантується, і компілятор повинен створити принаймні одне діагностичне повідомлення (попередження або помилка) відповідно до стандарту C.

Зауважте, що є нульовим покажчиком на мові С. Це не має значення для основної архітектури. Якщо основна архітектура має нульове значення вказівника, визначене як адреса 0xDEADBEEF, то сортування цього безладу залежить від компілятора.

Як такий, навіть у цій смішній архітектурі такі способи перевірки наявності нульового вказівника все ще є наступними способами:

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

Нижче наведено INVALID способи перевірки нульового вказівника:

#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
Sinan Ünür

4
Ні, ви не будете порівнювати ptrз усіма бітами-нулями . Це не a memcmp, але це порівняння з використанням вбудованого оператора. Одна сторона - це нульова константа вказівника '\0', а друга сторона - вказівник. Так само, як і в інших двох версіях з NULLі 0. Ті троє роблять те саме.
Йоханнес Шауб - ліб

6
Ви приймаєте вбудований оператор порівняння як предмет, який би порівнював бітові рядки. Але це не так. Він порівнює два значення, які є абстрактними поняттями. Таким чином , покажчик нуля , що внутрішньо представляються як 0xDEADBEEFпо - , як і раніше є покажчик NULL, незалежно від того , що подобається його бітовий зовнішній вигляд, і він буде по- , як і раніше вважається рівним NULL, 0, \0і все іншими постійними формами покажчика нуля.
Йоханнес Шауб - ліб

2
Ви добре оціните оператора порівняння. Я вичесав C99. Він говорить "Ціле постійне вираз зі значенням 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')і здивуйтеся.
caf

3
@Nasko: Я був дуже здивований: з gcc, в C: sizeof ('\ 0') == sizeof ('a') == 4, тоді як з g ++, в C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas

1
@Nasko: Із стандарту C (чернетка, n1124): "Ціла константа символів має тип int", таким чином "\ 0" є фактично типу int в C, і таким чином sizeof ('\ 0') в моїй архітектурі дорівнює 4 (linux, 32bit)
David Rodríguez - dribeas

@dribeas - Я не описував це як константу, а саме те, що ви бачили б як частина рядка. Я, безумовно, міг би зробити це явним. Спасибі
Насько

@ DavidRodríguez-dribeas Скасувати змінити "Виправлено" 0 "значення ASCII до 0x20 (32 грудня)"
chux - Відновити Моніку

6

Якщо NULL і 0 еквівалентні нульовим константам покажчика, що я повинен використовувати? у списку поширених запитань C також розглядається це питання:

Програмісти повинні розуміти , що NULLі 0є взаємозамінними в стрілочних контекстах, і що uncast 0 цілком прийнятно. Будь-яке використання NULL (на відміну від 0) слід вважати ніжним нагадуванням про те, що задіяний покажчик; програмісти не повинні залежати від цього (ні для їх власного розуміння, ні від компілятора) для відмежування покажчиків 0від цілих чисел 0.

І лише в контекстах покажчика це NULLі 0є рівнозначно. NULLне слід застосовувати, коли 0потрібен інший вид , навіть якщо це може спрацювати, оскільки це надсилає неправильне стилістичне повідомлення. (Крім того, ANSI дозволяє NULLвизначити ((void *)0), що бути , яке взагалі не буде працювати в не-покажчикових контекстах.) Зокрема, не використовуйте, NULLколи потрібний нульовий символ ASCII ( NUL). Надайте власне визначення

#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-розрядна :

Компілятор VC ++ від Microsoft використовує модель 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

@caf це залежить від мови. Якщо ви мені не вірите, спробуйте sizeof('\0')компілятор C ++.
Євген Йокота

2
ви повинні використовувати "% zu" під час друку sizeof (щось)
Невикористане


4

Хороший твір, який допомагає мені починати з C (взято з програмування експертів на C Linden)

Єдиний 'я' нуль і два 'л' недійсний

Запам'ятайте цю маленьку риму, щоб згадати правильну термінологію для покажчиків та нуля 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". Два терміни не мають взаємозамінного значення.


Набагато простіше: 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.


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 - це макрос, і шкідливий кодер може переосмислити його як щось інше.
Девід Родрігес - дрибес

6
І NULL гарантовано дорівнює 0. Бітова картина вказівника NULL не гарантовано буде всіма нулями, але константа NULL є і завжди буде 0.
jalf

2
Ваше перше речення неправильне - NULL не можна визначити як (void *) 0 у C ++, оскільки немає неявного перетворення з пустоти * в інший покажчик (на відміну від C).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.