(C) Segmentation fault, aber wieso?
*Markus
- sonstiges
Hallo,
ich habe eine Struktur, die ich als Datentyp verwenden will. Daraufhin möchte ich ein Array mit diesen Typen erzeugen und den einzelnen Elementen Werte zuweisen. Mein ursprünglichen Programm wäre zu umfangreich, somit habe ich ein Miniprogramm mit dem gleichen Problem nachgebaut:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct tag {
char* vname;
char* nname;
int age;
};
typedef struct tag* Person;
void show_name(struct tag *p);
extern char* strdup(const char *s1);
int main(void) {
//2 Personen anlegen
Person* personen;
//Hiermir soll simuliert werden, dass name1 und name2 bereits Werte haben wenn sie den Personen übergeben werden
char* vname1 = "Markus";
char* nname1 = "Müller";
char* vname2 = "Thomas";
char* nname2 = "Mustermann";
personen = (Person *)calloc(2, sizeof(Person));
printf("Größe einer Person: %d\n", sizeof(Person));
printf("Größe des Personenarrays: %d\n", sizeof(personen));
printf("vname1: %s\n", vname1);
personen[0]->vname = (char *)strdup(vname1);
personen[0]->nname = (char *)strdup(nname1);
personen[0]->age = 20;
personen[1]->vname = (char *)strdup(vname2);
personen[1]->nname = (char *)strdup(nname2);
personen[1]->age = 30;
for (int i = 0; i < 2; i++)
show_name(personen[i]);
return 0;
}
void show_name(Person p) {
printf("%s\n ", p->vname);
printf("%s \n", p->nname);
printf("%d \n", p->age);
}
Der Speicherzugriffsfehler tritt bereits hier auf:
personen[0]->vname = (char *)strdup(vname1);
Ich komme einfach nicht dahinter, wie ich vname einen Wert zuweisen kann, da immer ein Speicherzugriffsfehler entsteht. strdup alloziert bereits Speicher. Daran kann es also nicht liegen. Selbst mit strcpy oder einer Direktzuweisung bekomme ich Speicherzugriffsfehler. Das kann doch nicht so schwer sein? Außerdem ist sizeof(personen) ebenso 4, wie auch sizeof(Person). Wieso?
Markus.
Hallo Markus,
typedef struct tag* Person;
[...]
personen = (Person *)calloc(2, sizeof(Person));
[...]
personen[0]->vname = (char *)strdup(vname1);
Ich darf das mal etwas anders schreiben:
struct tag **personen = (struct tag **)calloc(2, sizeof(struct tag *));
Damit allozierst Du einen Speicherbereich, in den zwei Zeiger passen und weist die Adresse dieses Speicherbereichs an die Variable personen zu, die selbst nur ein Zeiger ist.
Die Zeiger im Speicherbereich sind jedoch (wg. calloc) mit 0 initialisiert, d.h. Du versuchst in der Zeile personen[0]-> den NULL-Zeiger zu dereferenzieren. Du müsstest in Deinem Fall vorher noch separate Speicherbereiche allozieren, die die Struktur selbst enthalten, damit Du die Struktur dann befüllen kannst. Also:
personen = (struct tag **)calloc(2, sizeof(struct tag *));
personen[0] = (struct tag *)calloc(1, sizeof(struct tag));
personen[1] = (struct tag *)calloc(1, sizeof(struct tag));
/* ab *jetzt* ist personen[0] gültig! */
personen[0]->vname = (char *)strdup(vname1);
/* ... */
Allerdings: In diesem Fall kommt mir das *extrem* umständlich vor, Du willst ja offensichtlich nur ein Array haben - warum dann ein Array auf Zeiger auf die Struktur und nicht ein Array der Struktur selbst?
struct tag *personen = (struct tag *)calloc(2, sizeof(struct tag));
personen[0].vname = (char *)strdup(vname1);
/* ... */
Außerdem ist sizeof(personen) ebenso 4, wie auch sizeof(Person). Wieso?
Weil beides Zeiger sind und Du auf einem 32bit-System (32 Bit == 4 Bytes) arbeitest - und sizeof eben die Größe der Variable oder des Typen liefert und nicht die Größe dessen, worauf die Variable oder der Typ zeigt. Wenn Du sizeof(struct tag) machst, dann erhälst Du auch die korrekte Größe der Struktur (was in Deinem Fall vmtl. 12 wäre).
Btw: Nix für ungut, aber Du solltest Dir wirklich langsam mal ein gutes Buch zum Thema C zulegen. Denn anscheinend können die Antworten, die wir Dir hier geben, Dir keine Klarheit über das Zeiger-Konzept in C schaffen.
Viele Grüße,
Christian
Hallo,
danke für die Hinweise. Jetzt funktioniert es. Ich dachte die ganze Zeit, dass ich mit der ersten Allokation bereits Platz für die Personen direkt geschaffen hatte. Aber jetzt ist klar, warum es nicht funktionieren konnte.
Btw: Nix für ungut, aber Du solltest Dir wirklich langsam mal ein gutes Buch zum Thema C zulegen. Denn anscheinend können die Antworten, die wir Dir hier geben, Dir keine Klarheit über das Zeiger-Konzept in C schaffen.
Im Großen und Ganzen denke ich, dass ich das meiste begriffen habe. Obwohl ich zugeben muss, dass kompliziertere Gebilde (zB Dereferenzieren von Werten von Arrays von Zeigern auf Strukturen, die wiederum Zeiger auf char, o.ä. enthalten) ab und zu schon die eine oder andere Unsicherheit auslösen.
Ich besorgte mir auch das Buch "Programmieren in C" von Kernighan und Richtie, aber obwohl mir das Buch gute Tipps vermittelte, konnte es mir auch nicht mehr Know How über Zeiger verschaffen, als ich vorher nicht ohnehin schon hatte. Außerdem machte ich wieder mal den Fehler, die deutsche Version eines Fachbuchs zu wählen, anstatt die Orginalversion, wodurch das Lernen daraus aufgrund der dämlichen Übersetzung unnötig erschwert wurde.
Ich frage mich welche Leute solche Bücher übersetzen und gebräuchliche Fachbegriffe eindeutschen und noch dazu falsch. Informatiker sind das wohl ganz bestimmt nicht, und wenn, dann keine, die je eine Zeile Code geschrieben haben.
Viele Grüße,
Markus
Hallo Markus,
danke für die Hinweise. Jetzt funktioniert es. Ich dachte die ganze Zeit, dass ich mit der ersten Allokation bereits Platz für die Personen direkt geschaffen hatte. Aber jetzt ist klar, warum es nicht funktionieren konnte.
Das ist übrigens, warum ich typedefs auf Zeigertypen überhaupt nicht mag, weil es Informationen vor einem versteckt, was dann zu Problemen führt. Es gibt natürlich Ausnahmen, wo sie sinnvoll sind, in Deinem Fall würde ich aber definitiv (egal ob Du nun Array von Zeigern oder Array von Strukturen willst) das typedef nur auf die Struktur machen, nicht auf den Zeiger.
Ich besorgte mir auch das Buch "Programmieren in C" von Kernighan und Richtie, aber obwohl mir das Buch gute Tipps vermittelte, konnte es mir auch nicht mehr Know How über Zeiger verschaffen, als ich vorher nicht ohnehin schon hatte.
Hmm, seltsam, ich hab vor Ewigkeiten mit dem Buch C gelernt (auch in der dt. Übersetzung) und fast alles, was ich über Zeiger in C weiß, habe ich daher...
Viele Grüße,
Christian
Tag,
Ich besorgte mir auch das Buch "Programmieren in C" von Kernighan und Richtie, aber obwohl mir das Buch gute Tipps vermittelte, konnte es mir auch nicht mehr Know How über Zeiger verschaffen, als ich vorher nicht ohnehin schon hatte.
Den "Kernighan und Richtie" finde ich zum C-Lernen auch nicht besonders gut muss ich sagen. Man merkt halt, dass es von den zwei Machern von C ist - es zeigt recht kurz, prägnant und anschaulich, was C kann und wie man es benutzt.
Aber es ist didaktisch nicht als "Lehrbuch" aufgebaut sondern mehr eine Sprachdefinition.
-> Ich finde, es ist als Nachschlagewerk ein Must-Have, aber als Tutorial...ich weiß nicht. Ein gutes Lehrbuch kann ich allerdings auch nicht empfehlen.
Auch bei mir hat das Verständnis mit dem Zeigerkram ziemlich lang auf sich warten lassen. Irgendwann, bei einer größeren C-Übungsaufgabe an der Uni, die über ein ganzes Semester ging, hats *klick* gemacht...und plötzlich war ich (zumindest kams mir damals so vor ;)) der Zeigerkönig.
Aber vorher wohl eher der Zeiger-Chaot im Tal der Ahnungslosen :)
Also: Nur Geduld :)