Ce face Comportament nedefinit (undefined behavior)
Standardul C descrie ce face fiecare construcție corectă. Pentru unele operații greșite spune doar că „comportamentul este nedefinit” (undefined behavior, UB): compilatorul nu are nicio obligație. Programul poate afișa ce te aștepți, o valoare oarecare, se poate opri sau se poate comporta diferit cu alt compilator sau cu altă optimizare (-O2).
Cel mai periculos este că UB nu dă neapărat o eroare: optimizatorul presupune că UB nu apare niciodată și poate elimina verificări pe care le-ai scris. De exemplu, presupunând că x + 1 nu depășește niciodată INT_MAX, poate considera că x + 1 > x este mereu adevărat.
Cele mai frecvente cazuri: index în afara tabloului, dereferențierea lui NULL sau a unui pointer eliberat, depășirea la întregi cu semn, citirea unei variabile neinițializate, împărțirea la zero, modificarea unui literal de tip șir, modificarea aceleiași variabile de două ori într-o expresie fără ordine definită. Unelte care ajută: avertismentele (-Wall -Wextra) și sanitizerele (-fsanitize=address,undefined).
Exemple
Exemplul 1: Depășire la întregi: varianta corectă
Înainte de adunare se verifică dacă rezultatul încape în int. Pentru unsigned, depășirea este definită (se reia de la 0); doar la tipurile cu semn este UB. În C23 poți folosi și ckd_add din <stdckdint.h>.
#include <stdio.h>
#include <limits.h>
int aduna_sigur(int a, int b, int *rezultat) {
if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) return 0;
*rezultat = a + b;
return 1;
}
int main(void) {
int r;
if (aduna_sigur(2000000000, 100000000, &r)) printf("suma = %d\n", r);
else printf("suma nu încape în int\n");
unsigned int u = UINT_MAX;
u = u + 1; // definit: se reia de la 0
printf("UINT_MAX + 1 = %u\n", u);
return 0;
}
Rezultat
suma = 2100000000
UINT_MAX + 1 = 0
Exemplul 2: Index în afara tabloului (nu rula așa)
Bucla merge până la i <= 3, deci citește și note[3], care nu există. Compilatorul nu avertizează aici, iar programul pare să meargă: pe calculatorul de test, ultimul rând a fost 2, o valoare care nu există în tablou. Pe alt calculator poate fi orice altceva, iar programul se poate și opri.
#include <stdio.h>
int main(void) {
int note[3] = {9, 7, 10};
for (int i = 0; i <= 3; i++) { // GREȘIT: trebuie i < 3
printf("%d\n", note[i]);
}
return 0;
}
Rezultat posibil (comportament nedefinit)
9
7
10
2
Exemplul 3: Prinderea UB cu sanitizerul
Compilat cu -fsanitize=undefined, programul raportează depășirea în timpul rulării, cu fișierul și rândul. Folosește sanitizerele în timpul dezvoltării și al testelor.
// compilare: clang -std=c23 -fsanitize=undefined depasire.c -o depasire
#include <stdio.h>
#include <limits.h>
int main(void) {
int x = INT_MAX;
x = x + 1; // GREȘIT: depășire la int cu semn
printf("%d\n", x);
return 0;
}
Ieșire în terminal
depasire.c:7:11: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior depasire.c:7:11
-2147483648
Greșeli frecvente
- Crezi că dacă programul „merge” pe calculatorul tău, codul este corect. UB poate apărea doar cu alt compilator, altă optimizare sau alte date.
- Verifici depășirea după ce s-a produs (
if (a + b < a)pentruint). Depășirea în sine este deja UB, iar compilatorul poate elimina testul. Verifică înainte de operație. - Scrii
i = i++ + 1;saua[i] = i++;. Variabila este modificată și citită fără o ordine definită; desparte în instrucțiuni separate. - Ignori avertismentele compilatorului. Multe cazuri de UB (variabile neinițializate, indecși constanți greșiți, formate
printfgreșite) sunt semnalate de-Wall -Wextra.
Comenzi înrudite
Tablouri (array): Un șir de elemente de același tip, așezate unul după altul în memorie și accesate prin index, de la 0.Pointeri (& și *): O variabilă care păstrează adresa altei variabile; cu * citești sau modifici valoarea de la acea adresă.free: Eliberează un bloc alocat cu malloc, calloc sau realloc, ca memoria să poată fi refolosită.ckd_add, ckd_sub, ckd_mul: Adunare, scădere și înmulțire cu verificarea depășirii: funcțiile din stdckdint.h spun dacă rezultatul a încăput în tip.Compilarea cu gcc și clang: Cum transformi codul C într-un program executabil cu gcc sau clang și ce fac opțiunile folosite cel mai des.Operatori aritmetici (+ - * / % ++ --): Adunare, scădere, înmulțire, împărțire, restul împărțirii, atribuiri compuse și incrementare cu ++ / –.
Întrebări & Exemple de Cod:
Comportament nedefinit (undefined behavior)Ai o nelămurire despre utilizarea comenzii
Comportament nedefinit (undefined behavior)sau ai descoperit un truc / fragment de cod util? Distribuie-l cu ceilalți programatori!Fii primul care pune o întrebare sau distribuie un fragment de cod / prompt!
Nu au fost adăugate încă întrebări sau exemple de cod pentru această pagină. Dacă ai întâlnit o problemă sau ai un snippet util, adaugă-l mai jos pentru a-i ajuta pe ceilalți.