2008-09-23 13 views
7

Also habe ich einige C-Code bekam:Warum löst Strcpy einen Segmentierungsfehler mit globalen Variablen aus?

#include <stdio.h> 
#include <string.h> 

/* putting one of the "char*"s here causes a segfault */ 
void main() { 
    char* path = "/temp"; 
    char* temp; 
    strcpy(temp, path); 
} 

Dies kompiliert, läuft, und verhält sich, wie es aussieht. Wenn jedoch einer oder beide Zeichenzeiger als globale Variable deklariert sind, führt strcpy zu einem Segmentierungsfehler. Warum passiert das? Offensichtlich gibt es einen Fehler in meinem Verständnis von Umfang.

+0

Da ich glaube nicht, dass es das Problem lösen wird, werde ich nur kommentieren, dass Strncpy über Strcpy empfohlen wird. –

+0

Josh Gagnon: Strncpy platziert tatsächlich keinen Null-Terminator, wenn die Länge der Eingabestring> = der Puffer ist. strcpy ist vollkommen sicher, wenn Sie wissen, dass der Puffer groß genug ist. Verwenden Sie andernfalls 'snprintf (buffer, buffer_len,"% s ", src)', da snprintf immer einen Null-Terminator eingibt (stellen Sie nur sicher, dass buffer_len> 0 ist). –

+0

@Josh: Ich bevorzuge 'strlcpy'. Leider unterstützt glibc es nicht, so dass ich keine Chance bekomme, es zu benutzen. Ich nehme an, ich könnte immer meine eigene Implementierung rollen und sie zu meiner persönlichen Header-Bibliothek von Null-Überprüfung malloc und dateibezogenen Funktionen hinzufügen, aber es ärgert mich immer noch, dass viele Versionen von Unix es haben, während Linux das normalerweise nicht tut. – JAB

Antwort

16

Wie andere Poster erwähnt, ist die Wurzel des Problems, dass Temp nicht initialisiert ist. Wenn es auf dem Stapel als automatische Variable deklariert wird, enthält es den Müll, der sich an diesem Speicherort befindet. Anscheinend ist für den Compiler + CPU + OS, den Sie ausführen, der Müll an dieser Stelle ein gültiger Zeiger. Der strcpy "ist erfolgreich", da er nicht segfault ist, aber tatsächlich hat er einen String an eine beliebige Stelle irgendwo im Speicher kopiert. Diese Art von Speicherkorruptionsproblemen bringt überall Angst in die Herzen von C-Programmierern, da es außerordentlich schwierig zu debuggen ist.

Wenn Sie die temporäre Variablendeklaration in den globalen Bereich verschieben, wird sie in den BSS-Abschnitt eingefügt und automatisch auf Null gesetzt. Versuche zur Dereferenzierung von * temp führen dann zu einem segfault.

Wenn Sie den * Pfad zum globalen Bereich verschieben, bewegt sich * temp um eine Position auf dem Stapel nach oben. Der Müll an diesem Ort ist offensichtlich kein gültiger Zeiger und daher führt Dereferenzierung * temp zu einem segfault.

+0

Wie erwartet, wechselt die Reihenfolge der Variablendeklarationen das Programm zu segfault. Vielen Dank! – codemonkey

8

Sie haben vergessen, Temp zuzuordnen und initialisieren:

temp = (char *)malloc(TEMP_SIZE); 

So stellen Sie sicher TEMP_SIZE groß genug ist. Sie können dies auch zur Laufzeit berechnen, dann stellen Sie sicher, dass die Größe genug ist (mindestens strlen (Pfad) werden)

+0

Es ist nicht notwendig, den Speicher bei temp zu initialisieren - strcpy kümmert sich darum, auch wenn nur der anfängliche Nullbegriff gesetzt wird. –

+0

Zusätzlich muss es mindestens strlen (Pfad) +1 sein, um den Null-Ausdruck zu passen, oder es entstehen schlechte Dinge, die T (M) ergeben. –

+0

Er meint, dass Sie die Zeile "temp [0] = 0" nicht benötigen, da strcpy() den NULL-Terminator für Sie hinzufügt. –

1

Der wichtige Teil zu beachten:
Zielstring dest muss groß genug sein, die erhalten Kopieren.
In Ihrer Situation hat temp keinen Speicher zum Kopieren in.

von der Handbuchseite von strcpy kopiert hat:

DESCRIPTION 
    The strcpy() function copies the string pointed to by src (including 
    the terminating '\0' character) to the array pointed to by dest. The 
    strings may not overlap, and the destination string dest must be large 
    enough to receive the copy. 
9

die Variable TEMP verweist nicht auf jeden Speicher (Speicher), und es ist nicht initialisierte.

Wenn temp als char temp[32]; deklariert wird, dann würde der Code funktionieren, egal wo er deklariert ist. Es gibt jedoch andere Probleme mit der Deklaration von Temp mit einer festen Größe wie diese, aber das ist eine Frage für einen anderen Tag.

Nun, warum stürzt es ab, wenn es global und nicht lokal deklariert wird. Luck ...

Bei einer lokalen Deklaration kommt der Wert von temp von dem Wert, der sich zu diesem Zeitpunkt auf dem Stack befinden könnte. Es ist Glück, dass es auf eine Adresse zeigt, die keinen Absturz verursacht. Es wird jedoch Speicher gelöscht, der von jemand anderem verwendet wird.

Wenn global deklariert, werden diese Variablen in den meisten Prozessoren in Datensegmenten gespeichert, die Seiten mit Bedarf Null verwenden. So erscheint char *temp, als ob es char *temp=0 deklariert wurde.

1

Sie rufen ein undefiniertes Verhalten auf, da Sie die Variable temp nicht initialisieren. Es zeigt auf einen zufälligen Speicherort im Speicher, so dass Ihr Programm funktioniert, aber höchstwahrscheinlich wird es segfault. Sie müssen Ihre Zielstring haben ein Array sein, oder haben es auf dynamische Speicher zeigen:

// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, 256, path); 

// Or, use dynamic memory 
char *temp = (char *)malloc(256); 
strncpy(temp, 256, path); 

, verwenden auch strncpy() statt strcpy() Pufferüberschreitungen zu vermeiden.

+0

Adam, ist es eine gute Idee, all diese magischen Zahlen herumschwimmen zu lassen? ;-) –

+0

, die immernoch Bugs hat, da die Source-Zeichenkette eine Länge von 256 hat, dann haben die Zeichenketten keine Null-Terminierung. – Torlack

+0

Es ist nicht erforderlich, den von malloc zurückgegebenen Zeiger zu konvertieren - malloc gibt einen void-Zeiger zurück, und daher ist keine Umwandlung erforderlich. Darüber hinaus könnte es Sie verletzen, da es Probleme mit malloc nicht richtig definiert (wie die implizite Definition von malloc gibt es ein int) an dieser Codezeile verbergen. –

3

Wie oben erwähnt, haben Sie vergessen, Platz für Temp zu reservieren. Ich bevorzuge strdup bis malloc+strcpy. Es macht, was du machen willst.

+0

Ich wusste nichts über straddup. Vielen Dank. – codemonkey

2

Nein - das funktioniert nicht unabhängig von den Variablen - es sieht nur so aus, als hätte man (un) Glück gehabt. Sie müssen Speicherplatz reservieren, um den Inhalt der Zeichenfolge zu speichern, anstatt die Variable nicht initialisiert zu lassen.

Nicht initialisierte Variablen auf dem Stapel werden auf ziemlich zufällige Speicherorte zeigen.Wenn diese Adressen gültig sind, trampelt Ihr Code überall herum, aber es wird kein Fehler angezeigt (aber Sie können an anderer Stelle in Ihrem Code böse Fehler bekommen, die mit der Speicherbeschädigung zusammenhängen).

Globale Fehler treten regelmäßig auf, da sie normalerweise auf bestimmte Muster festgelegt werden, die auf nicht zugeordneten Speicher verweisen. Wenn Sie versuchen, diese zu dereferenzieren, erhalten Sie sofort einen segfault (was besser ist - wenn Sie es später lassen, wird der Fehler sehr schwer zu finden sein).

2

würde Ich mag ersten Adams Fragment als

Sie
// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, sizeof(temp), path); 
temp[sizeof(temp)-1] = '\0'; 

auf diese Weise neu zu schreiben:

1. don't have magic numbers laced through the code, and 
2. you guarantee that your string is null terminated. 

Der zweite Punkt ist bei dem Verlust der letzten Zeichen der Quellzeichenfolge, wenn es > = 256 Zeichen lang.

+0

Bei der Verwendung von wchar_t funktioniert das nicht. Die Verwendung von sizeof funktioniert nur mit Zeichen. Ich weiß, das ist eine Frage über Zeichen, aber ich stoße auf viele Bugs, wo Leute sizeof mit wchar_t benutzen. – Torlack