Ce face nullptr
Un pointer nul nu indică spre niciun obiect. Până la C23 se scria NULL, o macro care, în funcție de sistem, este 0 sau ((void*)0). C23 adaugă cuvântul cheie nullptr, o constantă care este mereu pointer nul și are propriul tip, nullptr_t (declarat în <stddef.h>).
nullptr se poate atribui oricărui tip de pointer și se poate compara cu orice pointer, dar nu se poate pune într-un int: compilatorul refuză. Cu NULL definit ca 0, greșeala ar fi trecut neobservată. Diferența contează și în funcțiile cu argumente variabile și în _Generic, unde tipul argumentului este important.
Ca și înainte, a citi sau a scrie prin pointerul nul (*p când p == nullptr) este comportament nedefinit; de obicei programul se oprește. Verifică pointerul înainte să-l folosești.
Exemple
Exemplul 1: Funcție care returnează nullptr când nu găsește
cauta returnează adresa elevului sau nullptr. Apelantul verifică rezultatul înainte să-l folosească.
#include <stdio.h>
#include <string.h>
struct elev {
const char *nume;
int nota;
};
static struct elev *cauta(struct elev clasa[], int n, const char *nume) {
for (int i = 0; i < n; i++) {
if (strcmp(clasa[i].nume, nume) == 0) {
return &clasa[i];
}
}
return nullptr;
}
int main(void) {
struct elev clasa[] = {{"Ana", 10}, {"Radu", 8}};
const char *cautati[] = {"Radu", "Elena"};
for (int i = 0; i < 2; i++) {
struct elev *e = cauta(clasa, 2, cautati[i]);
if (e == nullptr) {
printf("%s nu este în catalog\n", cautati[i]);
} else {
printf("%s are nota %d\n", e->nume, e->nota);
}
}
return 0;
}
Rezultat
Radu are nota 8
Elena nu este în catalog
Exemplul 2: Tipul nullptr_t și _Generic
_Generic alege o ramură după tipul expresiei. nullptr are tipul nullptr_t, diferit de int și de void *, deci poate fi recunoscut.
#include <stddef.h>
#include <stdio.h>
#define TIP(x) _Generic((x), \
nullptr_t: "nullptr_t", \
int: "int", \
void *: "void *", \
default: "alt tip")
int main(void) {
void *adresa = nullptr;
printf("nullptr -> %s\n", TIP(nullptr));
printf("0 -> %s\n", TIP(0));
printf("adresa -> %s\n", TIP(adresa));
printf("sizeof(nullptr_t) = %zu\n", sizeof(nullptr_t));
return 0;
}
Rezultat
nullptr -> nullptr_t
0 -> int
adresa -> void *
sizeof(nullptr_t) = 8
Exemplul 3: nullptr nu se poate pune într-un int
Compilatorul refuză să inițializeze un int cu nullptr. Cu NULL definit ca 0, aceeași linie s-ar fi compilat fără mesaj.
int main(void) {
int *p = nullptr; // corect
int n = nullptr; // greșit: n nu este pointer
return n + (p != nullptr);
}
Mesajul compilatorului (programul nu se compilează)
exemplu.c:3:9: error: initializing 'int' with an expression of incompatible type 'nullptr_t'
3 | int n = nullptr; // greșit: n nu este pointer
| ^ ~~~~~~~
1 error generated.
Greșeli frecvente
- Crezi că
nullptrface pointerul sigur. Nu face: dacă folosești*pfără să verifici, programul se oprește la fel ca înainte. - Eliberezi memoria cu
free(p)și continui să foloseștip. Pointerul încă are adresa veche (pointer „atârnat”). Dupăfree, scriep = nullptr;. - Folosești
nullptrîn cod care trebuie compilat și cu standarde mai vechi. Înainte de C23 nu există; acolo rămâi laNULL.
Comenzi înrudite
Pointeri (& și *): O variabilă care păstrează adresa altei variabile; cu * citești sau modifici valoarea de la acea adresă.malloc: Cere sistemului un bloc de memorie de mărimea dată, în timpul rulării, și returnează adresa lui sau NULL dacă nu reușește.free: Eliberează un bloc alocat cu malloc, calloc sau realloc, ca memoria să poată fi refolosită.void * (pointer generic): Un pointer care poate păstra adresa oricărui tip de date, dar care trebuie convertit la un tip concret înainte de folosire.bool, true, false: Tipul pentru valori adevărat/fals. În C23, bool, true și false sunt cuvinte cheie, fără să mai incluzi stdbool.h.typeof și auto: typeof dă tipul unei expresii, iar auto deduce tipul variabilei din valoarea inițială. Ambele sunt standard din C23.