Razionale dietro le funzioni della libreria C che non impostano mai errno a zero

8

Lo standard C impone che nessuna funzione della libreria standard C debba impostare errno su zero. Perché è esattamente questo?

Potrei capire che è utile per chiamare diverse funzioni e controllare solo errno dopo l'ultima, ad esempio:

errno = 0;
double x = strtod(str1, NULL);
long y = strtol(str2, NULL);
if (errno)
    // either "strtod" or "strtol" failed
else
    // both succeeded

Tuttavia, questa non è considerata una "cattiva pratica"? Dato che stai solo controllando errno alla fine, sai solo che una delle funzioni ha fallisce, ma non quella quale fallita. Semplicemente sapere che qualcosa è fallito abbastanza bene per la maggior parte dei programmi pratici?

Ho provato a cercare vari documenti C Rationale, ma molti di loro non hanno molti dettagli per <errno.h> .

    
posta Drew McGowen 07.08.2013 - 23:15
fonte

3 risposte

10

La libreria C non imposta errno su 0 per ragioni storiche 1 . POSIX non rivendica più che le sue librerie non modificheranno il valore in caso di successo e la nuova pagina man di Linux per errno.h riflette questo:

The <errno.h> header file defines the integer variable errno, which is set by system calls and some library functions in the event of an error to indicate what went wrong. Its value is significant only when the return value of the call indicated an error (i.e., -1 from most system calls; -1 or NULL from most library functions); a function that succeeds is allowed to change errno.

La Razionale ANSI C afferma che il comitato si sentiva era più pratico adottare e standardizzare la pratica esistente dell'uso di errno .

The error reporting machinery centered about the setting of errno is generally regarded with tolerance at best. It requires a ''pathological coupling'' between library functions and makes use of a static writable memory cell, which interferes with the construction of shareable libraries. Nevertheless, the Committee preferred to standardize this existing, however deficient, machinery rather than invent something more ambitious.

C'è quasi sempre un modo per controllare l'errore al di fuori di controllare se è stato impostato errno . Controllare se errno è stato impostato non è sempre affidabile, poiché alcune chiamate richiedono di chiamare un'API separata per ottenere il motivo dell'errore. Ad esempio, ferror() viene utilizzato per verificare la presenza di un errore se ottieni un risultato breve da fread() o fwrite() .

È interessante notare che il tuo esempio di utilizzo di strtod() è uno dei casi in cui l'impostazione errno su 0 prima che la chiamata sia richiesta per rilevare correttamente se si è verificato un errore. Tutte le funzioni strto*() stringa su numero hanno questo requisito, poiché viene restituito un valore di ritorno valido anche a fronte di un errore.

errno = 0;
char *endptr;
double x = strtod(str1, &endptr);
if (endptr == str1) {
    /*...parse error */
} else if (errno == ERANGE) {
    if (x == 0) {
        /*...underflow */
    } else if (x == HUGE_VAL) {
        /*...positive overflow */
    } else if (x == -HUGE_VAL) {
        /*...negative overflow */
    } else {
        /*...unknown range error? */
    }
}

Il codice sopra riportato si basa sul comportamento di strtod() come documentato su Linux . Lo standard C stabilisce che l'underflow non può restituire un valore maggiore del più piccolo% positivo% co_de e se double è impostato su errno è l'implementazione definita 2 .

In realtà esiste un ampio articolo di consulenza sulla cert che raccomanda sempre impostando ERANGE su 0 prima di una chiamata alla libreria e verificandone il valore dopo la chiamata, si è verificato un errore . Questo perché alcune chiamate di libreria imposteranno errno anche se la chiamata stessa ha avuto successo 3 .

The value of errno is 0 at program startup, but it is never set to 0 by any library function. The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in the C Standard. It is meaningful for a program to inspect the contents of errno only after an error has been reported. More precisely, errno is meaningful only after a library function that sets errno on error has returned an error code.

1. In precedenza ho affermato che era per evitare di mascherare un errore da una chiamata precedente. Non riesco a trovare alcuna prova a sostegno di questa affermazione. Ho anche avuto un esempio falso errno .
2. Grazie a @chux per averlo indicato. Il riferimento è C.11 §7.22.1.3 ¶10.
3. Indicato da @KeithThompson in un commento.

    
risposta data 08.08.2013 - 00:33
fonte
1

Puoi fare un controllo degli errori per entrambe le chiamate di funzione, se davvero ti interessano.

errno = 0;
double x = strtod(str1, NULL);
if (errno)
    // strtod"  failed
else
    // "strtod" succeeded

long y = strtol(str2, NULL);
if (errno)
    // "strtol" failed
else
    // "strtol" succeeded

Poiché non sappiamo mai come la funzione mach ha chiamato in un processo, come può la lib impostare errnos per ogni chiamata di funzione, Right?

    
risposta data 08.08.2013 - 00:32
fonte
1

Cambiare arbitrariamente l'errorno è analogo a "prendere e deglutire" un'eccezione. Prima c'erano delle eccezioni che si propagavano attraverso diversi livelli di un programma e, infine, raggiungevano un punto in cui il chiamante riuscisse a catturare e rispondere all'eccezione in un modo o ad un semplice passaggio, c'erano errori. Non alterando l'errore, a meno che tu non stia gestendo in qualche modo, ignorando, trattando come irrilevante, l'errore, è essenziale per questo paradigma di propagazione della gestione degli errori di prima generazione.

    
risposta data 28.08.2013 - 17:39
fonte

Leggi altre domande sui tag