it-swarm.com.de

Absturz oder "Segmentierungsfehler" beim Kopieren/Scannen/Lesen von Daten in einen nicht initialisierten Zeiger

Diese Frage soll als Referenz für alle häufig gestellten Fragen der Natur dienen:

Warum erhalte ich einen mysteriösen Absturz oder "Segmentierungsfehler", wenn ich Daten an die Adresse kopiere/scanne, auf die ein nicht initialisierter Zeiger zeigt?

Zum Beispiel:

char* ptr;
strcpy(ptr, "hello world"); // crash here!

oder

char* ptr;
scanf("%s", ptr); // crash here!
36
Lundin

Ein Zeiger ist ein spezieller Variablentyp, der nur eine Adresse einer anderen Variablen enthalten kann. Es darf keine Daten enthalten. Sie können keine "Daten in einen Zeiger kopieren/speichern" - das ergibt keinen Sinn. Sie können nur einen Zeiger auf Daten setzen, die an anderer Stelle zugeordnet sind.

Dies bedeutet, dass ein Zeiger immer auf einen gültigen Speicherort zeigen muss, damit er aussagekräftig ist. Beispielsweise könnte es auf den auf dem Stapel zugewiesenen Speicher verweisen:

{
  int data = 0;
  int* ptr = &data;
  ...
}

Oder Speicher dynamisch auf dem Heap zugeordnet:

int* ptr = malloc(sizeof(int));

Es ist immer ein Fehler, einen Zeiger zu verwenden, bevor er initialisiert wurde. Es zeigt noch nicht auf einen gültigen Speicher.

Diese Beispiele können alle zu Programmabstürzen oder anderen unerwarteten Verhaltensweisen führen, z. B. "Segmentierungsfehlern":

/*** examples of incorrect use of pointers ***/

// 1.
int* bad;
*bad = 42;

// 2.
char* bad;
strcpy(bad, "hello");

Stattdessen müssen Sie sicherstellen, dass der Zeiger auf (genügend) zugewiesenen Speicher zeigt:

/*** examples of correct use of pointers ***/

// 1.
int var;
int* good = &var;
*good = 42;

// 2.
char* good = malloc(5 + 1); // allocates memory for 5 characters *and*  the null terminator
strcpy(good, "hello");

Beachten Sie, dass Sie einen Zeiger auch so einstellen können, dass er auf ein genau definiertes "Nirgendwo" zeigt, indem Sie ihn auf NULL zeigen lassen. Dies macht es zu einem Nullzeiger, einem Zeiger, der garantiert nicht auf einen gültigen Speicher zeigt. Dies unterscheidet sich davon, dass der Zeiger nicht initialisiert wird.

int* p1 = NULL; // pointer to nowhere
int* p2;        // uninitialized pointer, pointer to "anywhere", cannot be used yet

Wenn Sie jedoch versuchen, auf den Speicher zuzugreifen, auf den ein Nullzeiger zeigt, können ähnliche Probleme auftreten wie bei Verwendung eines nicht initialisierten Zeigers: Abstürze oder Segmentierungsfehler. Im besten Fall bemerkt Ihr System, dass Sie versuchen, auf die Adresse null zuzugreifen, und löst dann eine "Nullzeiger-Ausnahme" aus.

Die Lösung für Nullzeiger-Ausnahmefehler ist dieselbe: Sie müssen den Zeiger so einstellen, dass er auf einen gültigen Speicher zeigt, bevor Sie ihn verwenden.


Weitere Lektüre:

Zeiger auf ungültige Daten
Wie greife ich mit Zeigern von einer anderen Funktion auf eine lokale Variable zu?
Kann auf den Speicher einer lokalen Variablen außerhalb ihres Gültigkeitsbereichs zugegriffen werden?

Segmentierungsfehler und Ursachen
Was ist ein Segmentierungsfehler?
Warum erhalte ich einen Segmentierungsfehler, wenn ich in eine Zeichenfolge schreibe, die mit "char * s", aber nicht mit "char s []" initialisiert wurde?
Was ist der Unterschied zwischen char s [] und char * s?
Definitive Liste der häufigsten Gründe für Segmentierungsfehler
Was ist ein Busfehler?

32
Lundin
  1. Zeiger zeigen nur auf einen Speicherplatz. Sie haben einen Zeiger erstellt, sich aber noch nicht an einen Speicherort gebunden. strcpy möchte, dass Sie zwei Zeiger übergeben ( der erste darf nicht konstant sein ), die auf zwei Zeichen-Arrays wie diese Signatur zeigen:

    char * strcpy ( char * destination, const char * source );
    

    verwendungsbeispiel:

    char* ptr = malloc(32);  
    strcpy(ptr, "hello world");
    
    char str[32];  
    strcpy(str, "hello world");
    
  2. Sie können das folgende Code-Snippet verwenden, um die Zeichenfolge bis zum Erreichen des Zeilenumbruchs zu lesen (* Sie können auch andere Leerzeichen wie "%[^\t\n]s" ( tab, newline ) oder "%[^ \t\n]s" ( Leerzeichen, Tab, newline ) hinzufügen.

    char *ptr = malloc(32);
    scanf("%31[^\n]", ptr);
    

    (Vergessen Sie nicht, den Rückgabewert von scanf() zu überprüfen!)

2
lonesomecodeboy

Dies geschieht, weil Sie nicht für Speicher für den Zeiger char* ptr ..__ zugewiesen haben. In diesem Fall müssen Sie dynamisch zuweisen Speicher für den Zeiger.

Für dynamic memory allocation können zwei Funktionen malloc() und calloc() verwendet werden.

Versuchen Sie diesen Code: -

  char* ptr;
  ptr = (char *) malloc(sizeof(char)*50); // allocate space for 50 characters.
  strcpy(ptr, "hello world");

Wenn Sie *ptr verwenden, vergessen Sie nicht, Speicher freigeben für *ptr zuzuordnen. Dies kann mit der Funktion free() durchgeführt werden.

  free(ptr);  // deallocating memory.

Die Größe von dynamisch zugewiesener Speicher kann mit realloc() geändert werden.

  ptr = (char *)realloc(ptr, sizeof(char)*100);// allocate space for 0 characters.

In den meisten Fällen geschieht "Segmentierungsfehler" aufgrund eines Fehlers in Speicherzuordnung oder Array außerhalb der Bindung Fällen.

0
anoopknr

Anstatt malloc, strlen und strcpy zu verwenden, verfügt die POSIX C-Bibliothek über eine praktische Funktion mit dem Namen strdup in <string.h>, Das eine Kopie der übergebenen nullterminierten Zeichenfolge mit zugewiesener Speicherdauer zurückgibt. Nach Gebrauch sollte der Zeiger mit free freigegeben werden:

char* ptr;
ptr = strdup("hello world");
ptr[0] = 'H';
puts(ptr);
free(ptr);
0
Antti Haapala

Ist dies nicht der Fall, aber eine Situation, die zu Beginn häufig auftritt, ist die Verwendung von einfachen Anführungszeichen und der Versuch, ein String-Literal wie folgt zu definieren:

char ptr[5];
strcpy(ptr, 'hello'); // crash here!
            ^     ^   // because of ' instead "

In C ist "h" ein Literal für ein einzelnes Zeichen, während "h" ein String-Literal ist, das ein "h" und ein Null-Terminator/0 enthält (dh ein 2-Zeichen-Array). In C ist der Typ eines Zeichen-Literals also, dass sizeof 'h' 4 ist (auf 32bit), während sizeof (char) 1 ist.

char h = 'h';
printf("Size: %d\n",sizeof(h));     //Size: 1
printf("Size: %d\n",sizeof('h'));   //Size: 4
0