Reihenfolge verursacht Segmentation Fault?
-
Hallo!
Ich hoffe, ihr könnt mir helfen. Ich lerne gerade C und bin gerade dabei, mich mit Zeigern vertraut zu machen. Zur Übung wollte ich ein Programm schreiben, dass einen Vektor erzeugt. Es ist kurz, ich schreibe es hier einfach mal hin:
#include <stdio.h> #include <stdlib.h> void erzeuge_vektor(float *v, int dim); //Erzeugt einen Vektor und reserviert den noetigen Platz main() { float *v; //int i; erzeuge_vektor(v,50); printf("TEST\n"); } void erzeuge_vektor(float *v,int dim) { float *w; w = (float *) malloc(dim*sizeof(float)); if(NULL==w) { printf("Speicheranforderung gescheitert!\n"); } else { int i; for(i=0;i<dim;i++) { w[i]=3.0*i; *(v+i)=*(w+i); } } }
Wenn ich diesen Code mittels gcc zu kompilieren versuche, erhalte ich einen Segmentation Fault. Interessanterweise tritt dieser Fehler nicht auf, wenn ich die im Moment auskommentierte Zeile wieder einkommentiere, also eine sinnlose und nicht benutzte integer-Variable definiere.
Kann mir das jemand erklären?
Vielen Dank!
alaundo
-
Oh, entschuldigt bitte den Tippfehler, der mein "das" zu einem "dass" gemacht hat.
-
Hallo alaundo,
da du gerade dabei bist C zu lernen, gewöhne dir bitte an die main Funktion mit einer der beiden folgenden Signaturen zu schreiben:
int main(void) int main(int argc, char* argv[])
Alle anderen Schreibweisen sind kein Standard C!
Zu deinem Problem:
Du versuchts in Zeile 32 an der Speicherstelle von (v+i) einen Wert zuzuweisen, aber wo zeigt v denn überhaupt hin? Was du da tust ist sogenanntes „undefined behavior“; es kann alles passieren. Daher verändert auch die scheinbar sinnlose Zeile 9 das Verhalten des Progammes.
-
Ui, vielen Dank für die schnelle Antwort!
Diese beiden Schreibweisen kannte ich noch nicht - ich lerne gerade aus dem Buch Programmieren in C - eine mathematikorientierte Einführung, und dort sehen die main-Funktionen alle so aus.
Ich werde den Tipp aber beherzigen und mir das angewöhnen.
Kannst du mir helfen, dieses "undefined behaviour" in den Griff zu kriegen? Das Problem ist also, dass ich den Zeiger v erst initialisieren muss? Sollte ich dem auch Speicher zuweisen mit calloc oder malloc (aber dann verbrauche ich den Speicher ja doppelt, oder?)? Oder sollte ich auf w verzichten?
Gruß
alaundo
-
Du willst ja den in Deiner Funktion allokierten Speicher in Deiner Hauptfunkion über den Zeiger v ansprechen können.
Du musst also in Deiner Funktion den Wert des Zeigers v ändern.
Dazu muss die Adresse des Zeigers übergeben werden:void erzeuge_vektor(float **v, int dim);
In der Funktion weist Du dann den allokierten Speicher so zu:
*v = malloc ...
Die Variable w ist überflüssig.
Aufgerufen wird die Funktion dann mit der Adresse von v:
float *v; //int i; erzeuge_vektor(&v,50);
-
alaundo schrieb:
Kannst du mir helfen, dieses "undefined behaviour" in den Griff zu kriegen? Das Problem ist also, dass ich den Zeiger v erst initialisieren muss? Sollte ich dem auch Speicher zuweisen mit calloc oder malloc (aber dann verbrauche ich den Speicher ja doppelt, oder?)? Oder sollte ich auf w verzichten?
Wozu meinst du w zu brauchen?
-
@Belli
Aha, aber das bedeutet ja, dass ich für ein eindimensionales Feld "Vektor" einen Zeiger auf einen Zeiger brauche. In den Beispielen, die ich mir in dem Buch angesehen habe, wurden für Vektoren Zeiger und für Matrizen, also zweidimensionale Felder, Zeiger auf Zeiger benutzt.@Bashar
Das kommt von einem vorherigen Beispiel, auch aus dem Buch. Ich schreibe es mal dazu, weil es kurz ist. Es ging darum, den Fehler in folgendem Programm zu beheben (die main-Funktion war so angegeben, nicht meine Schuld):
#include <stdio.h> #include <stdlib.h> void erzeuge_vektor(double *v, int dim); main() { double *v; int i; erzeuge_vektor(v,50); for(i=0;i<50;i++) { if(i%5==0)printf("\n"); printf("%lf ",v[i]); } printf("\n"); } void erzeuge_vektor(double *v, int dim) { int i; v = (double *)malloc(dim*sizeof(double)); if(NULL==v) printf("Speicheranforderung gescheitert!\n"); else for(i=0;i<dim;i++) { v[i]=1.0; } }
Das Problem hier war, dass die Werte in v nicht über die Lebensdauer der Unterfunktion hinaus erhalten blieben, sondern v im Hauptprogramm mit irgendwelchen Werten gefüllt war. Das hatte ich mit einer zusätzlichen Zeigervariable w gelöst, die ich lokal gefüllt hatte und dann v darauf referenziert.
Aber wahrscheinlich war das auch ungeschickt und anders viel besser zu lösen
-
Das Problem ist dasselbe.
Du bekommst so v nicht aus erzeuge_vektor raus.In C werden nur Kopien der Werte übergeben. (Bei Arrays eine Kopie der Adresse vom ersten Element)
Wenn du ein Objekt aus einer Funktion heraus ändern willst, musst du dessen Adresse wissen. Darum nimmt man Zeiger.Du willst jetzt aber nicht das ändern worauf v zeigt, sondern du willst v selber ändern.
Darum musst du die Adresse von v übergeben.Ein Zeiger ist eine Variable, die eine Adresse aufnimmt.
alaundo schrieb:
die main-Funktion war so angegeben, nicht meine Schuld
Das schreibt man so seit 25 Jahren nicht mehr. Und das Buch soll von 2007 sein
-
alaundo schrieb:
@Belli
Aha, aber das bedeutet ja, dass ich für ein eindimensionales Feld "Vektor" einen Zeiger auf einen Zeiger brauche.Nein. In main hast Du nach wie vor einen einfachen Zeiger. Nur wenn Du dessen Wert in einer Funktion ändern willst, musst Du seine Adresse an die Funktion übergeben, hast also in der Funktion dann einen Zeiger auf einen Zeiger ...
-
Danke euch allen, ich glaube ich habe es inzwischen verstanden. Das Programm läuft jetzt jedenfalls so. Ich werde aber mal noch ein bisschen herumprobieren, um zu sehen, ob ich es wirklich verstanden habe.
Im Prinzip kann ich mir (für den Anfang!) als Faustregel merken, dass wenn ich einen Wert eines Objekts verändern will, mit Adressen und Zeigern arbeiten muss? Wenn das Objekt selbst schon ein Zeiger ist, habe ich eben einen Zeiger auf einen Zeiger.
Das schreibt man so seit 25 Jahren nicht mehr. Und das Buch soll von 2007 sein
Oh je, hab ich da bei dem Buch eine schlechte Wahl getroffen? Ich hatte gedacht bei einem noch nicht so alten Buch sollten keine so groben Schnitzer drin sein...
Auf jeden Fall vielen Dank an alle!
alaundo