GNU/Linux >> Linux Esercitazione >  >> Linux

Perché fclose(NULL) di glibc causa un errore di segmentazione invece di restituire un errore?

fclose richiede come argomento un FILE puntatore ottenuto da fopen , uno dei flussi standard stdin , stdout o stderr o in qualche altro modo definito dall'implementazione. Un puntatore nullo non è uno di questi, quindi il comportamento non è definito, proprio come fclose((FILE *)0xdeadbeef) sarebbe. NULL non è speciale in Do; a parte il fatto che è garantito il confronto non uguale a qualsiasi puntatore valido, è proprio come qualsiasi altro puntatore non valido e il suo utilizzo richiama un comportamento indefinito tranne quando l'interfaccia lo stai passando ai documenti come parte del suo contratto che NULL ha un significato speciale.

Inoltre, tornare con un errore sarebbe valido (poiché il comportamento è comunque indefinito) ma un comportamento dannoso per un'implementazione, perché nasconde il comportamento indefinito . Il risultato preferibile dell'invocazione di un comportamento indefinito è sempre un arresto anomalo, perché evidenzia l'errore e consente di correggerlo. La maggior parte degli utenti di fclose non verificare la presenza di un valore restituito di errore e scommetto che la maggior parte delle persone così sciocche da passare NULL a fclose non saranno abbastanza intelligenti da controllare il valore di ritorno di fclose . Si potrebbe argomentare che le persone dovrebbero controlla il valore di ritorno di fclose in generale, poiché il flush finale potrebbe fallire, ma questo non è necessario per i file aperti solo in lettura, o se fflush è stato chiamato manualmente prima di fclose (che è comunque un linguaggio più intelligente perché è più facile gestire l'errore mentre hai ancora il file aperto).


fclose(NULL) dovrebbe avere successo. free(NULL) riesce, perché questo rende più facile scrivere il codice di pulizia.

Purtroppo, non è così che è stato definito. Pertanto non puoi usare fclose(NULL) nei programmi portatili. (Ad esempio, vedi http://pubs.opengroup.org/onlinepubs/9699919799/).

Come altri hanno già detto, in genere non si desidera che venga restituito un errore se si passa NULL nel posto sbagliato. Vuoi un messaggio di avviso, almeno nelle build di debug/test. Dereferenziare NULL ti dà un messaggio di avviso immediato e l'opportunità di raccogliere un backtrace che identifica l'errore di programmazione :). Mentre stai programmando, un segfault è il miglior errore che puoi ottenere. C ha molti errori più sottili, che richiedono molto più tempo per il debug...

È possibile abusare dei ritorni di errore per aumentare la robustezza rispetto agli errori di programmazione. Tuttavia, se sei preoccupato che un arresto anomalo del software possa perdere dati, tieni presente che può accadere esattamente lo stesso, ad es. se il tuo hardware perde potenza. Ecco perché abbiamo il salvataggio automatico (poiché gli editor di testo Unix con nomi di due lettere come ex e vi). Sarebbe comunque preferibile che il tuo software si bloccasse visibilmente, piuttosto che continuare con uno stato incoerente.


Gli errori di cui parla la pagina man sono errori di runtime, non errori di programmazione. Non puoi semplicemente superare NULL in qualsiasi API che si aspetta un puntatore e si aspetta che l'API faccia qualcosa di ragionevole. Passaggio di un NULL puntatore a una funzione documentata per richiedere un puntatore ai dati è un bug.

Domanda correlata:in C o C++, devo controllare i parametri del puntatore rispetto a NULL/nullptr?

Per citare il commento di R. su una delle risposte a questa domanda:

... sembra che tu stia confondendo errori derivanti da condizioni eccezionali nell'ambiente operativo (fs pieno, memoria esaurita, rete inattiva, ecc.) con errori di programmazione. Nel primo caso, ovviamente un programma robusto deve essere in grado di gestirli con garbo. In quest'ultimo, un programma robusto non può sperimentarli in primo luogo.


Linux
  1. Errore C++:riferimento non definito a 'clock_gettime' e 'clock_settime'

  2. Perché connect() dovrebbe dare EADDRNOTAVAIL?

  3. Il modo più semplice per individuare un errore di segmentazione

  4. Perché alcuni programmatori del kernel usano goto invece di semplici cicli while?

  5. Perché ENOENT significa No such file or directory?

Perché uso exa invece di ls su Linux

Perché viene visualizzato l'errore:snap "xyz" non trovato?

Errore Gparted - Errore di segmentazione (core dumped)?

Linux vs Mac OS:15 motivi per utilizzare Linux invece di Mac OS

Perché eval dovrebbe essere evitato in Bash e cosa dovrei usare invece?

Errore di segmentazione quando Qt QApplication è stata creata con new