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».
Якщо брати конкретну реалізацію NULL за сирцевими файлами, то він може бути визначеним як (void*)0 або як 0. Використання NULL у проектах мовою C++ може призводити до помилок. Наприклад
int (ClassName::*pf)() = NULL;
призведе до помилки компіляції в разі, якщо NULL визначено як (void*)0 (наприклад опосередковано включено заголовок, де стандартне для C++ визначення NULL перекривається). Тому в програмах на C++ не рекомендується використовувати NULL у вигляді ((void*) 0). У стандарті C++11 для позначення нульового вказівника додано нове ключове слово nullptr[2].
- ↑ N 2394 (PDF). Open Standards (English) . Архів (PDF) оригіналу за 27 липня 2020. Процитовано 22 травня 2020.
- ↑ 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.