programowanieBackend C programista

Jak wdrożyć obsługę błędów podczas pracy z funkcjami standardowej biblioteki C? Jaka jest specyfika użycia errno, kiedy lepiej używać kodów zwrotu i jak uniknąć błędów podczas obsługi?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

Obsługa błędów w języku C zawsze była zadaniem programisty. W standardowej bibliotece nie ma wyjątków, błędy są zwracane przez kody zwrotu lub globalną zmienną errno (początek użycia — UNIX lat 70-tych, następnie POSIX i ANSI C). Takie mechanizmy są stosowane i dziś do zarządzania przepływem wykonania w sytuacjach awaryjnych.

Problem

Błędy podczas pracy ze standardowymi funkcjami (operacje na plikach, przydzielanie pamięci, funkcje łańcuchowe) mogą być niezauważalne bez odpowiedniej kontroli. Niewłaściwa obsługa — ignorowanie kodu zwrotu, niewłaściwa interpretacja errno, brak oczyszczania zasobów — prowadzi do błędnej pracy programu, awarii i podatności na ataki.

Rozwiązanie

Prawidłowa obsługa błędów wymaga obowiązkowej analizy wartości zwracanych przez funkcje, użycia errno tylko bezpośrednio po awarii i informacyjnego wyświetlania błędów. Kody zwrotu są preferowane dla funkcji wewnętrznych — pozwalają na obsługę bez globalnych efektów ubocznych. errno jest częściej stosowane z wywołaniami systemowymi i funkcjami standardowej biblioteki. Po każdej potencjalnie niebezpiecznej operacji analizowany jest zwrot, a globalny stan (errno) nie powinien być nadpisywany przez pośrednie wywołania.

Przykład kodu:

#include <stdio.h> #include <errno.h> #include <string.h> FILE *open_file(const char *filename) { errno = 0; FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Błąd: %s ", strerror(errno)); } return f; }

Kluczowe cechy:

  • errno jest używane tylko do diagnostyki błędów, które wystąpiły w wywołaniach systemowych/funkcjach standardowych.
  • Wewnątrz funkcji wygodnie jest zwracać kody błędów int, zero — sukces, liczba ujemna lub wartości specjalne — błąd.
  • Ważne jest, aby nie polegać na wartości errno, jeśli przed tym nie było błędu.

Pytania z podstępem.

Czy można używać errno dla funkcji użytkownika, jeśli chce się przekazywać błędy do góry?

Nie, errno jest przeznaczona tylko do standardowych wywołań bibliotecznych i systemowych. Jest globalna, może być nadpisana w dowolnym punkcie i nie nadaje się dla własnych funkcji.

Czy należy ustawiać errno przed każdym wywołaniem funkcji?

Nie, ale zaleca się resetowanie errno (na przykład do zera) przed wywołaniami, jeśli planowane jest analizowanie zmian. Nie każda funkcja zmienia errno przy sukcesie, tylko przy błędzie.

eerrno = 0; ... wywołanie niebezpiecznej funkcji ...

Czy można ufać errno po każdej funkcji?

Tylko dla tych funkcji, które zgodnie z normą wyraźnie ustawiają ją przy niepowodzeniu. Wiele funkcji standardowej biblioteki nie modyfikuje errno przy sukcesie. Dokumentacja to twój przyjaciel.

Typowe błędy i antywzorce

  • Ignorowanie kodu zwrotu przy funkcjach (np. fopen, malloc, write).
  • Nadpisywanie errno przed jej użyciem.
  • Używanie errno do własnych błędów w projekcie.

Przykład z życia

Negatywny przypadek

Otwieranie pliku bez sprawdzania wyniku, błędy nie są analizowane, program działa błędnie przy braku pliku:

Zalety:

  • Mniej kodu, szybko działa przy prostych scenariuszach.

Wady:

  • Kryptyczne awarie przy błędach, brak możliwości diagnostyki.

Pozytywny przypadek

Po każdej krytycznej funkcji wynik jest sprawdzany, w przypadku błędu wyświetlana jest szczegółowa wiadomość z strerror(errno), wykonanie kończy się prawidłowo:

Zalety:

  • Program jest łatwy do konserwacji i debugowania, wysoka stabilność.

Wady:

  • Nieco więcej kodu, nieco większa złożoność.