Ausführung von printf() und Segmentierungsfehler

Lesezeit: 4 Minuten

Benutzer-Avatar
Vikram

#include<stdio.h>

int main()
{
    char *name = "Vikram";
    printf("%s",name);
    name[1]='s';
    printf("%s",name);
    return 0;
}

Es wird keine Ausgabe auf dem Terminal gedruckt und es wird nur ein Segmentierungsfehler angezeigt. Aber wenn ich es in GDB ausführe, bekomme ich Folgendes –

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400525 in main () at seg2.c:7
7       name[1]='s';
(gdb) 

Dies bedeutet, dass das Programm einen SEG-Fehler in der 7. Zeile erhält (offensichtlich kann ich nicht auf ein konstantes Zeichenarray schreiben). Warum wird dann printf() in Zeile 6 nicht ausgeführt?

  • Ich bin mir nicht ganz sicher. Es funktioniert wie erwartet auf meinem Mac mit OSX Lion (entspricht LLVM, debuggt mit LLDB).

    – Richard J.Ross III

    27. Februar 2012 um 18:02 Uhr

Dies liegt an der Stream-Pufferung von stdout. Außer du machst fflush(stdout) oder Sie drucken einen Zeilenumbruch "\n" die Ausgabe kann gepuffert werden.

In diesem Fall ist es ein Segfaulting, bevor der Puffer geleert und gedruckt wird.

Sie können stattdessen Folgendes versuchen:

printf("%s",name);
fflush(stdout);        //  Flush the stream.
name[1]='s';           //  Segfault here (undefined behavior)

oder:

printf("%s\n",name);   //  Flush the stream with '\n'
name[1]='s';           //  Segfault here (undefined behavior)

  • Beachten Sie, dass fflush ist wirklich der richtige Weg, es zu tun – ein Zeilenumbruch löst garantiert keinen Flush aus (und ich bin von diesem Verhalten schon einmal gebissen worden).

    – Aaron Dufour

    27. Februar 2012 um 18:30 Uhr

  • Übrigens, puts(name) ist äquivalent zu printf("%s\n",name);, obwohl einige Leute den impliziten Zeilenumbruch nicht mögen. Compiler optimieren die printf-Puts für Sie.

    – Peter Cordes

    31. Dezember 2021 um 7:48 Uhr


  • Das Einschließen eines Zeilenumbruchs löscht nur implizit ohne fflush für zeilengepufferte Standardausgabe nicht vollständig gepuffert; Wenn Sie stdout in eine Datei umleiten, wird sie vollständig gepuffert. stderr ist immer ungepuffert; das ist ein Grund zu bevorzugen fprintf(stderr, ...) für Debug-Drucke um Segfaults, wenn Sie keinen Zeilenumbruch einfügen oder das Protokoll in eine Datei umleiten möchten und nicht nach jedem Aufruf auch fflush verwenden möchten.

    – Peter Cordes

    31. Dezember 2021 um 7:53 Uhr


Benutzer-Avatar
paul

Zuerst sollten Sie Ihre printfs mit “\n” (oder zumindest dem letzten) beenden. Aber das hat nichts mit dem Segfault zu tun.

Wenn der Compiler Ihren Code kompiliert, teilt er die Binärdatei in mehrere Abschnitte auf. Einige sind schreibgeschützt, während andere beschreibbar sind. Das Schreiben in einen schreibgeschützten Abschnitt kann einen Segfault verursachen. Zeichenfolgenliterale werden normalerweise in einem schreibgeschützten Abschnitt platziert (gcc sollte sie in „.rodata“ einfügen). Der Zeigername zeigt auf diesen ro-Abschnitt. Daher müssen Sie verwenden

const char *name = "Vikram";

In meiner Antwort habe ich ein paar “kann” “sollte” verwendet. Das Verhalten hängt von Ihrem Betriebssystem, Compiler und den Kompilierungseinstellungen ab (Das Linker-Skript definiert die Abschnitte).

Hinzufügen

-Wa,-ahlms=myfile.lst

an die Befehlszeile von gcc erzeugt eine Datei namens myfile.lst mit dem generierten Assembler-Code. Oben sieht man

    .section .rodata
.LC0:
    .string "Vikram"

Was zeigt, dass die Zeichenfolge in Vikram ist.

Derselbe Code mit (Muss sich im globalen Bereich befinden, sonst kann gcc ihn auf dem Stack speichern, beachten Sie, dass es sich um ein Array und nicht um einen Zeiger handelt.)

char name[] = "Vikram";

produziert

    .data
    .type name, @object
    .size name, 7
name:
    .string "Vikram"

Die Syntax ist ein bisschen anders, aber sehen Sie, wie es jetzt im Abschnitt .data ist, der Lese-Schreiben ist. Dieses Beispiel funktioniert übrigens.

  • Wenn Sie bemerken, fragt das OP nicht, warum der Segfault auftritt, sondern warum die Zeichenfolge überhaupt nicht gedruckt wurde.

    – Richard J.Ross III

    27. Februar 2012 um 18:39 Uhr

  • Obwohl dies möglicherweise nicht genau die Frage beantwortet, ist der Tipp und die Erklärung zu .rodata und .data hilfreich.

    – vts

    8. Mai 2014 um 16:16 Uhr

Der Grund, warum Sie einen Segmentierungsfehler erhalten, ist, dass C-String-Literale nur gemäß dem C-Standard gelesen werden und Sie versuchen, ‘s’ über das zweite Element des Literal-Arrays “Vikram” zu schreiben.

Der Grund, warum Sie keine Ausgabe erhalten, liegt darin, dass Ihr Programm seine Ausgabe puffert und abstürzt, bevor es die Möglichkeit hat, seinen Puffer zu leeren. Der Zweck der stdio-Bibliothek besteht neben der Bereitstellung benutzerfreundlicher Formatierungsfunktionen wie printf(3) darin, den Overhead von I/O-Operationen zu reduzieren, indem Daten in In-Memory-Puffern gepuffert und Ausgaben nur bei Bedarf geleert und Eingaben nur gelegentlich ausgeführt werden statt ständig. Die tatsächliche Ein- und Ausgabe erfolgt im Allgemeinen nicht in dem Moment, in dem Sie die stdio-Funktion aufrufen, sondern erst, wenn der Ausgabepuffer voll (oder der Eingabepuffer leer) ist.

Die Dinge sind etwas anders, wenn ein FILE-Objekt so eingestellt wurde, dass es ständig löscht (wie stderr), aber im Allgemeinen ist das das Wesentliche.

Wenn Sie debuggen, ist es am besten, fprintf an stderr zu senden, um sicherzustellen, dass Ihre Debug-Ausdrucke vor einem Absturz geleert werden.

Standardmäßig wann stdout an ein Terminal angeschlossen ist, wird der Stream zeilengepuffert. In der Praxis ist in Ihrem Beispiel das Fehlen von '\n' (oder eines expliziten Stream Flush) ist der Grund, warum Sie die Zeichen nicht gedruckt bekommen.

Aber theoretisch ist undefiniertes Verhalten nicht begrenzt (aus dem Standard “Verhalten […] für die diese Internationale Norm keine Anforderungen stellt”) und der segfault kann sogar auftreten, bevor das undefinierte Verhalten auftritt, beispielsweise vor dem ersten printf Anruf!

1365840cookie-checkAusführung von printf() und Segmentierungsfehler

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

Privacy policy