NULL (C)

NULL у мовах програмування Сі і C++ — макрос, оголошений у заголовному файлі stddef.h (та інших заголовних файлах). Значенням цього макроса є залежна від реалізації константа нульового вказівника (англ. null pointer constant).

Константа нульового вказівника — це цілочисельний константний вираз зі значенням 0 або (тільки в Сі) такий самий вираз, але зведений до типу void*. Константа нульового вказівника, зведена до будь-якого типу вказівників, є нульовим вказівником. Гарантується, що нульовий вказівник не дорівнює вказівнику на будь-який об'єкт (в широкому сенсі слова, будь-які дані) або функцію. Гарантується, що будь-які два нульових вказівники рівні між собою. Розіменування нульового вказівника є операцією з невизначеною поведінкою.

Інакше кажучи, реалізація надає спеціальне значення — константу нульового вказівника, яку можна присвоїти будь-якому вказівнику і такий вказівник під час порівняння не дорівнюватиме будь-якому «правильному» вказівнику. Тобто, можна вважати, що нульовий вказівник не містить правильної адреси в пам'яті.

Використання

Нульові вказівники придумані як зручний спосіб «позначити» вказівники, які не вказують на правильну адресу в пам'яті. Наприклад, під час оголошення вказівника як автоматичної змінної його значення не визначене. Щоб позначити, що цей вказівник ще не містить правильної адреси в пам'яті, такому вказівнику присвоюють константу нульового вказівника:

void f(void)
{
 int *x = NULL;
 /* ... */
}

Хорошим стилем програмування є присвоювання вказівнику після вивільнення пам'яті, на яку він посилався, нульового вказівника. Крім цього, застосування обнулення вказівників актуальне для безпеки вивільнення пам'яті: операція delete в C++ (free в Сі) безпечна для нульового вказівника. Наприклад:

TYPE *foo = new TYPE();
// використання foo
delete foo;// foo != NULL
// якийсь код програми
delete foo;// ПОМИЛКА! Память вже недоступна

тоді як у такому варіанті помилки не буде

TYPE *foo = new TYPE();
// використання foo
delete foo;// foo != NULL
foo = NULL;// foo == NULL
// якийсь код програми
delete foo;// помилки немає: delete перевіряє значення foo

Розмірність вказівника

Під час виклику функції в один з аргументів можна передати NULL. Макрос NULL у різних компіляторах можна визначити різними способами, зокрема

#define NULL 0

#define NULL ((void *)0)

У першому випадку NULL має тип int, а в другому — тип void*. Існують архітектури, де sizeof (int) != sizeof (void*), тоді на різних платформах до функції надходитиме різна кількість байтів, що може порушити її роботу. Нині робиться спроба вирішити цю проблему в Сі введенням nullptr, див. пропозицію N 2394[1].

Розіменовування нульових вказівників

Розіменування нульового вказівника є операцією з невизначеною поведінкою. На реалізацію не накладається жодних обмежень: може статися, наприклад, звернення до пам'яті, не призначеної для використання даною програмою (тобто при читанні буде прочитано «сміття», а при записі — значення потрапить в ділянку пам'яті, що не належить програмі). Наприклад, у DOS запис за нульовою адресою затре принаймні нульовий вектор переривань, так що наступний виклик int 0 призведе, найпевніш, до зависання системи. Однак найчастіше це призводить до помилки часу виконання (якщо в операційній системі реалізовано захист пам'яті і доступ до невиділеної процесу пам'яті блокується). Наприклад, у Windows 9x повідомлення «Загальна помилка захисту» — «Програма виконала неприпустиму операцію і буде закрита» (англ. general protection fault, GPF) виводиться найчастіше в тих випадках, коли програма звертається до пам'яті за некоректним (зокрема й неініціалізованим або вже звільненим) вказівником. В UNIX-подібних операційних системах у таких ситуаціях процес отримує сигнал SIGSEGV і його опрацьовувач виводить повідомлення «Segmentation fault».

Нульові вказівники в C++

Якщо брати конкретну реалізацію NULL за сирцевими файлами, то він може бути визначеним як (void*)0 або як 0. Використання NULL у проектах мовою C++ може призводити до помилок. Наприклад

int (ClassName::*pf)() = NULL;

призведе до помилки компіляції в разі, якщо NULL визначено як (void*)0 (наприклад опосередковано включено заголовок, де стандартне для C++ визначення NULL перекривається). Тому в програмах на C++ не рекомендується використовувати NULL у вигляді ((void*) 0). У стандарті C++11 для позначення нульового вказівника додано нове ключове слово nullptr[2].

Див. також

Примітки

  1. N 2394 (PDF). Open Standards (English) . Архів (PDF) оригіналу за 27 липня 2020. Процитовано 22 травня 2020.
  2. JTC1/SC22/WG21 — The C++ Standards Committee (2 жовтня 2007). SC22/WG21/N2431 = J16/07-0301 «A name for the null pointer: nullptr» (PDF). JTC1.22.32 (англ.). The C++ Standards Committee. Архів оригіналу (PDF) за 11 лютого 2012. Процитовано 4 жовтня 2010.(англ.)

Посилання

  • Question 5.1. c-faq.com. Архів оригіналу за 26 лютого 2013. Процитовано 2 квітня 2022.
  • Question 5.13. c-faq.com. Архів оригіналу за 18 серпня 2011. Процитовано 2 квітня 2022.
Перегляд цього шаблону
  Довідкові видання
Fandom (англ.)
Нормативний контроль
GKG: /g/1224nkjf