Norma C nakazuje, aby żadne funkcje biblioteki standardowej C nie były ustawione errno
na zero. Dlaczego to dokładnie jest?
Mogłem zrozumieć, że jest to przydatne do wywoływania kilku funkcji i sprawdzania dopiero errno
po ostatniej - na przykład:
errno = 0;
double x = strtod(str1, NULL);
long y = strtol(str2, NULL);
if (errno)
// either "strtod" or "strtol" failed
else
// both succeeded
Czy nie jest to jednak uważane za „złą praktykę”? Skoro jesteś sprawdzanie tylko errno
na samym końcu, trzeba tylko wiedzieć, że jedna z funkcji nie powiedzie, ale nie które Błąd funkcji. Czy po prostu wiedza, że coś zawiodło, wystarcza dla większości praktycznych programów?
Próbowałem szukać różnych dokumentów uzasadnienia C, ale wiele z nich nie zawiera zbyt wielu szczegółów <errno.h>
.
errno
, zawsze możesz sam ustawić zero.errno
mieć wartość niezerową, nawet jeśli się powiedzie. (Może wywoływać inne funkcje, które zawodzą, ale nie stanowią awarii funkcji zewnętrznej).Odpowiedzi:
Biblioteka C nie jest ustawiona
errno
na 0 z powodów historycznych 1 . POSIX nie twierdzi już, że jego biblioteki nie zmienią wartości w przypadku sukcesu, a nowa strona podręcznika systemowego Linuxaerrno.h
odzwierciedla to:Uzasadnienie ANSI C stwierdza, że komitet uznał za bardziej praktyczne przyjęcie i ujednolicenie istniejącej praktyki używania
errno
.Prawie zawsze istnieje sposób na sprawdzenie błędu poza sprawdzeniem, czy
errno
został ustawiony. Sprawdzanie, czyerrno
ustawiony zestaw nie zawsze jest wiarygodne, ponieważ niektóre połączenia wymagają wywołania oddzielnego interfejsu API, aby uzyskać przyczynę błędu. Na przykładferror()
służy do sprawdzania błędu, jeśli otrzymasz krótki wynik zfread()
lubfwrite()
.Co ciekawe, przykładem użycia
strtod()
jest jeden z przypadków, w których ustawienie wartościerrno
0 przed wywołaniem jest wymagane do prawidłowego wykrycia błędu. Wszystkie funkcjestrto*()
ciąg do liczby mają ten wymóg, ponieważ poprawna wartość zwracana jest zwracana nawet w przypadku wystąpienia błędu.Powyższy kod opiera się na zachowaniu
strtod()
zgodnie z dokumentacją w systemie Linux . Norma C stanowi jedynie, że niedomiar nie może zwrócić wartości większej niż najmniejszy dodatnidouble
, a to, czyerrno
jest ustawiona,ERANGE
jest zdefiniowane jako implementacja 2 .W rzeczywistości istnieje obszerny zapis doradczy certyfikatu, który zaleca zawsze ustawienie
errno
na 0 przed wywołaniem biblioteki i sprawdzanie jego wartości po wywołaniu wskazującym na awarię . Wynika to z faktu, że niektóre wywołania biblioteki zostaną ustawione,errno
nawet jeśli samo wywołanie zakończyło się pomyślnie 3 .1. Wcześniej twierdziłem, że ma to na celu uniknięcie maskowania błędu z wcześniejszego połączenia. Nie mogę znaleźć żadnych dowodów na poparcie tego roszczenia. Miałem też fałszywy
printf()
przykład.2. Dzięki @chux za zwrócenie na to uwagi. Odniesieniem jest C.11 § 7.22.1.3 ¶10.
3. Wskazane przez @KeithThompson w komentarzu.
źródło
errno
sięERANGE
jest wdrożenie zdefiniowane w przypadku niedopełnienia, w rzeczywistości nie ma przenośny sposób wykryć niedomiar. Mój kod był zgodny z tym, co znalazłem na stronie podręcznika systemu Linux w moim systemie.strto*
funkcji są dokładnie powodem, dla którego spytałem, czy mój przykład byłby uważany za złą praktykę, ale jest to jedyny sposób, w którymerrno
nieustawienie wartości zero byłoby przydatne, a nawet zastosowanie.errno
można ustawić nawet w przypadku sukcesu, więc samo użycie tego, że zostało ustawione, nie jest tak naprawdę dobrym wskaźnikiem, że wystąpił błąd. Musisz sprawdzić wyniki poszczególnych połączeń, aby wiedzieć, czy w ogóle wystąpił błąd.Możesz naprawdę sprawdzić błędy obu wywołań funkcji, jeśli naprawdę Ci na tym zależy.
Ponieważ nigdy nie wiemy, jak wywołała się funkcja mach w procesie, w jaki sposób lib może ustawić errnos dla każdego wywołania funkcji, prawda?
źródło
Arbitralna zmiana błędu no jest analogiczna do „łapania i połykania” wyjątku. Zanim zdarzały się wyjątki, które rozprzestrzeniałyby się przez różne warstwy programu i ostatecznie osiągnęły punkt, w którym osoba dzwoniąca albo złapie i zareaguje na wyjątek w jakiś sposób, albo po prostu przejdzie za niego, były błędy. Nie zmienianie errno, chyba że w jakiś sposób obsługujesz, zastępujesz, traktujesz jako nieistotny błąd, jest niezbędny do tego paradygmatu propagacji obsługi błędów pierwszej generacji.
źródło