W języku C brak jest wbudowanej obsługi wyjątków, obsługa błędów realizowana jest w następujący sposób:
Zwracanie kodu błędu (zwykle przez wartość funkcji) Przykład:
int read_file(const char *name) { FILE *f = fopen(name, "r"); if (!f) return -1; // Błąd // ... fclose(f); return 0; // Sukces }
Użycie globalnej zmiennej errno
W przypadku błędu standardowe funkcje często ustawiają globalną zmienną errno.
Przykład:
FILE *fp = fopen("nofile", "r"); if (!fp) { perror("Błąd pliku"); // errno określa przyczynę }
setjmp/longjmp — mechanizm rzucania "wyjątków" (kontext wykonania jest zapisywany i przywracany) Stosowany w skomplikowanych przypadkach (np. międzywarstwowe awaryjne zakończenie). Używany rzadko, ponieważ komplikuje kod i może prowadzić do wycieków zasobów.
#include <setjmp.h> jmp_buf buf; if (setjmp(buf) == 0) { // ... longjmp(buf, 1); // Przechodzi do if (setjmp(...)) } else { // Obsługa błędu }
Co wyświetli poniższy kod?
#include <stdio.h> #include <setjmp.h> jmp_buf buf; void func() { longjmp(buf, 5); } int main() { int val = setjmp(buf); printf("val=%d\n", val); if (val == 0) func(); return 0; }
Odpowiedź:
setjmp zwraca 0 i wywołuje funkcję func.longjmp(buf, 5), wykonanie powraca do setjmp, który teraz zwraca 5.val=0
val=5
Historia W jednej bibliotece zrealizowano ręczny mechanizm czyszczenia zasobów w przypadku błędu tylko na podstawie kodu zwrotu. Ale programista zapomniał o obsłudze błędu przy zwrocie nie z wątku głównego, a z callbacka (przez setjmp/longjmp), w rezultacie wystąpiły wycieki pamięci i blokady plików.
Historia W aplikacji sieciowej pominięto sprawdzenie
errnopo operacji socket. W rezultacie wystąpiła fałszywa diagnoza: zmiennaerrnozachowała stare, nieprawidłowe wartości z poprzedniej funkcji.
Historia Zespół zaimplementował skomplikowaną zagnieżdżoną logikę z wieloma punktami zwrotu kodu błędu, ale nie przestrzegali konwencji dotyczącej wartości zwracanych (zero/ujemne wartości). Niektóre błędy były interpretowane jako udane, co prowadziło do nieprzewidywalnych awarii usług.