Warum verursacht fclose(NULL) von glibc einen Segmentierungsfehler, anstatt einen Fehler zurückzugeben?

Lesezeit: 6 Minuten

Benutzer-Avatar
Vdragon

Laut Manpage fclose(3):

RÜCKGABEWERT

Bei erfolgreichem Abschluss wird 0 zurückgegeben. Andernfalls, EOF zurückgegeben und die globale Variable errno gesetzt, um den Fehler anzuzeigen. In jedem Fall ist jeder weitere Zugriff (einschließlich eines weiteren Anrufs auf fclose()) an den Stream führt zu undefiniertem Verhalten.

FEHLER

EBADF Der zugrunde liegende Dateideskriptor fp ist ungültig.

Das fclose() Funktion kann auch fehlschlagen und eingestellt werden errno für alle Fehler, die für die Routinen angegeben sind close(2), write(2) oder fflush(3).

Na sicher fclose(NULL) sollte fehlschlagen, aber ich erwarte, dass es mit einem zurückkehrt errno normalerweise, anstatt direkt durch Segmentierungsfehler zu sterben. Gibt es einen Grund für dieses Verhalten?

Danke im Voraus.

UPDATE: Ich werde meinen Code hier einfügen (ich versuche es strerror()im Speziellen).

FILE *not_exist = NULL;

not_exist = fopen("nonexist", "r");
if(not_exist == NULL){
    printError(errno);
}

if(fclose(not_exist) == EOF){
    printError(errno);
}

  • Mögliches Duplikat von fclose(), das einen Segmentierungsfehler verursacht

    – Jim fiel

    13. November 2015 um 22:22 Uhr

fclose erfordert als Argument a FILE Zeiger erhalten entweder durch fopeneiner der Standardstreams stdin, stdoutoder stderr, oder auf eine andere implementierungsdefinierte Weise. Ein Nullzeiger gehört nicht dazu, daher ist das Verhalten undefiniert, genau wie fclose((FILE *)0xdeadbeef) wäre. NULL ist nichts Besonderes in C; Abgesehen von der Tatsache, dass es garantiert ungleich mit jedem gültigen Zeiger vergleicht, ist es genau wie jeder andere ungültige Zeiger, und seine Verwendung ruft ein undefiniertes Verhalten auf, außer wenn die Schnittstelle, die Sie es als Teil seines Vertrags an Dokumente übergeben, dies tut NULL hat eine besondere Bedeutung.

Außerdem wäre die Rückgabe mit einem Fehler gültig (da das Verhalten sowieso undefiniert ist), aber schädliches Verhalten für eine Implementierung, weil es verbirgt das undefinierte Verhalten. Das bevorzugte Ergebnis des Aufrufs von undefiniertem Verhalten ist immer ein Absturz, da es den Fehler hervorhebt und es Ihnen ermöglicht, ihn zu beheben. Die meisten Benutzer von fclose Suchen Sie nicht nach einem Fehlerrückgabewert, und ich würde wetten, dass die meisten Leute töricht genug sind, um zu bestehen NULL zu fclose werden nicht schlau genug sein, den Rückgabewert von zu überprüfen fclose. Man könnte argumentieren, dass Menschen sollte Überprüfen Sie den Rückgabewert von fclose in der Regel, da der letzte Flush fehlschlagen könnte, aber dies ist nicht notwendig für Dateien, die nur zum Lesen geöffnet werden, oder wenn fflush wurde vorher manuell aufgerufen fclose (Das ist sowieso eine klügere Redewendung, weil es einfacher ist, den Fehler zu behandeln, während Sie die Datei noch geöffnet haben).

  • Es “verbirgt” den Fehler in dem Sinne, dass das Programm ohne Hinweis darauf, dass etwas nicht stimmt, weiterläuft. Dieses Verstecken ist aktiv statt durch Unterlassung weil der Code den Zeiger aktiv gegen NULL testen musste, um einen Absturz zu vermeiden. Dies ist ein schlechteres Verhalten, als die Prüfung überhaupt nicht durchzuführen, da es das Debuggen des Programms erschwert. Wenn es sofort abstürzte (das Standardverhalten), würden Sie den Fehler sofort sehen und ihn beheben. Mit der Prüfung auf null (und dem Scheitern des Absturzes oder Abbruchs mit einem Assertionsfehler) wird dem fehlerhaften Zustand erlaubt, fortzubestehen/weiterzureichen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    10. März 2014 um 18:17 Uhr


  • Interessant, dass dieser Argumentation nicht gefolgt wurde malloc/free wobei das Freigeben eines Nullzeigers vollkommen in Ordnung ist. Inkonsistent…

    – Thomas

    15. Juni 2014 um 2:51 Uhr

  • @Thomas: Die Voraussetzung für free Nullzeiger zu akzeptieren ist keine Implementierungswahl; es ist von der Sprache vorgeschrieben. Einige Leute behaupten, dass diese Anforderung mit der Tatsache zusammenhängt, dass malloc(0) darf bei Erfolg einen Nullzeiger zurückgeben, aber ich habe keine offizielle Bestätigung dieser Behauptung gesehen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    15. Juni 2014 um 3:42 Uhr

  • Ich nehme an, die Frage ist, warum der C-Standard nicht erstellt wurde fclose(0); ein no-op, genau wie free(0);. Dies würde sicherlich keinen bestehenden Code beschädigen oder etwas Unerwünschtes verursachen.

    – PP

    18. August 2015 um 15:57 Uhr

  • Die Aussage ist im Klartext nicht richtig: ‘… Ein Nullzeiger gehört nicht dazu…’ Das ist nicht richtig. Nullzeiger ist ein Zeiger, der von zurückgegeben wird fopen wenn Datei nicht geöffnet werden kann. Nebenbei bemerkt, es wäre angemessen für fclose Null-Zeiger zu akzeptieren, verlangt Standard leider nicht.

    – SergejA

    16. Mai 2018 um 20:43 Uhr


fclose(NULL) sollte erfolgreich. free(NULL) erfolgreich ist, weil es dadurch einfacher wird, Bereinigungscode zu schreiben.

So wurde es leider nicht definiert. Daher können Sie nicht verwenden fclose(NULL) in portablen Programmen. (Siehe zB http://pubs.opengroup.org/onlinepubs/9699919799/).

Wie andere bereits erwähnt haben, möchten Sie im Allgemeinen keinen Fehler zurückgeben, wenn Sie NULL an die falsche Stelle übergeben. Sie möchten eine Warnmeldung, zumindest bei Debug-/Test-Builds. Die Dereferenzierung von NULL gibt Ihnen eine sofortige Warnmeldung und die Möglichkeit, einen Backtrace zu sammeln, der den Programmierfehler identifiziert :). Während Sie programmieren, ist ein Segfault der beste Fehler, den Sie bekommen können. C hat viel mehr subtile Fehler, deren Debugging viel länger dauert …

Es ist möglich, Fehlerrückgaben zu missbrauchen, um die Robustheit gegenüber Programmierfehlern zu erhöhen. Wenn Sie jedoch befürchten, dass ein Softwareabsturz Daten verlieren würde, beachten Sie, dass genau dasselbe passieren kann, zB wenn Ihre Hardware ausfällt. Deshalb haben wir Autosave (seit Unix-Texteditoren mit aus zwei Buchstaben bestehenden Namen wie ex und vi). Es wäre immer noch vorzuziehen, dass Ihre Software sichtbar abstürzt, anstatt mit einem inkonsistenten Zustand fortzufahren.

Benutzer-Avatar
Billy ONeal

Die Fehler, von denen die Manpage spricht, sind Laufzeitfehler, keine Programmierfehler. Du kannst nicht einfach passieren NULL in jede API, die einen Zeiger erwartet, und erwarten, dass diese API etwas Vernünftiges tut. Vorbei an a NULL Zeiger auf eine Funktion, die dokumentiert ist, dass sie einen Zeiger auf Daten erfordert, ist ein Fehler.

Verwandte Frage: Soll ich in C oder C++ Zeigerparameter gegen NULL/nullptr prüfen?

Um den Kommentar von R. zu einer der Antworten auf diese Frage zu zitieren:

… Sie scheinen Fehler, die sich aus außergewöhnlichen Bedingungen in der Betriebsumgebung ergeben (FS voll, Speichermangel, Netzwerkausfall usw.), mit Programmierfehlern zu verwechseln. Im ersteren Fall muss natürlich ein robustes Programm in der Lage sein, sie elegant zu handhaben. Bei letzterem kann ein robustes Programm sie gar nicht erst erfahren.

Benutzer-Avatar
Josef Quinsey

Dies fclose() Das Problem scheint ein Erbe von FreeBSD zu sein und wurde sowohl vom Microsoft- als auch vom Linux-Lager unkritisch akzeptiert.

Aber HP, SGI, Solaris und CYGWIN hingegen beherrschen alle fclose(NULL) vernünftig. Zum Beispiel, man fclose für CYGWIN, das newlib anstelle der glibc des OP verwendet, heißt es:

fclose gibt bei Erfolg 0 zurück (auch wenn FP NULL ist oder keine geöffnete Datei)

Siehe https://stackoverflow.com/a/8442421/318716 für eine verwandte Diskussion.

Ich denke, die Manpage spricht über Underlying Dateideskriptor (diejenige, die von ihm intern über die erhalten wird open Systemaufruf, wenn Sie anrufen fopen) ungültig ist, nicht die Dateizeiger zu dem du übergehst fclose.

1365380cookie-checkWarum verursacht fclose(NULL) von glibc einen Segmentierungsfehler, anstatt einen Fehler zurückzugeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy