Forum: PC-Programmierung C Segmentation fault


von Sir.Tom (Gast)


Lesenswert?

Hey!

ich spiele gerade etwas mit Struct's herum und habe nun versucht ein 
Array von structs zu erstellen.
So siehts aus:
1
struct etest {
2
  int err_id;
3
  int err_no;
4
  char err_name;
5
};
6
7
void main()
8
{
9
  struct etest *errstack[3];
10
11
  errstack[1]->err_id = 0x55;  // ein element testweise beschreiben.
12
13
  printf("id: %x\n",errstack[1]->err_id);

Mit dem Array index 1 wie im beispiel funktioniert das, nutze ich 
allerdings einen Index mit wert 0 läuft der compiler zwar ohne Fehler 
oder Warning durch, aber das Programm zeigt nur "Segmentation fault".

Woran kann dies liegen? Idee?

Ich nutze einen aktuellen gcc compiler unter ubuntu.

Vielen Dank und Gruß,
Thomas

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sir.Tom schrieb:
> habe nun versucht ein
> Array von structs zu erstellen.

Das aber tust Du nicht. Du hast ein Array von Pointern auf Structs 
erstellt, und da Du die Pointer nicht initialisierst, sie also im 
wahrsten Sinne des Wortes irgendwohin zeigen, schlagen Deine Versuche, 
auf die Strukturen zuzugreifen, verständlicherweise fehl.

von Martin Beuttenmüller (Gast)


Lesenswert?

Guten Tag ...

Hier mein Eigenversuch. Es compiliert und läuft fehlerfrei.
Zufall oder zufällig richtig ?
1
#include <stdio.h>
2
3
struct etest {
4
  int err_id;
5
  int err_no;
6
  char err_name;
7
};
8
9
10
void main()
11
{
12
  struct etest errstack;
13
14
  errstack.err_id = 0x55;  // ein element testweise beschreiben.
15
16
  printf("id: %x\n",errstack.err_id);
17
}

Für Genies sind Zeiger absolutes Muß,
für mich sind sie die Begründung für meine Depressionen ...

Martin

von Ralf (Gast)


Lesenswert?

> Zufall oder zufällig richtig ?
Für mich sieht's okay aus, allerdings...

> Für Genies sind Zeiger absolutes Muß,
> für mich sind sie die Begründung für meine Depressionen ...
... frage ich mich, wo du da einen Zeiger siehst?
Klar, intern wird da vielleicht einer verwendet, da es sich um ein 
struct handelt, aber du selbst verwendest da ja keinen.

Probier mal folgendes:
1
#include <stdio.h>
2
3
struct etest {
4
  int err_id;
5
  int err_no;
6
  char err_name;
7
};
8
9
10
void main()
11
{
12
  struct etest errstack;
13
  struct etest *errstack_p;
14
15
  errstack_p = &errstack;
16
17
  errstack_p->err_id = 0x55;  // ein element testweise beschreiben.
18
19
  printf("id: %x\n",errstack.err_id);
20
}
Achtung, ist aus dem Kopf, kann's grad nicht gegenprüfen, also evtl. mit 
Macken verbunden! Aber das wäre meiner Erinnerung nach ungefähr das, was 
für einen struct-Zugriff mittels Pointer nötig ist.

Ralf

von Karl H. (kbuchegg)


Lesenswert?

Martin Beuttenmüller schrieb:

> Für Genies sind Zeiger absolutes Muß,
> für mich sind sie die Begründung für meine Depressionen ...

Das hat mit Genie wenig zu tun.

Eher damit, dass man kapiert, das es einen Unterschied macht, ob ein 
Buch selber im Regal steht oder ob dort ein Zettel steckt auf dem steht 
"Buch gerade von Mitglied 'xyz' entlehnt. Zu finden in Gang 5, Büro 8".

von Klaus W. (mfgkw)


Lesenswert?

Martin Beuttenmüller schrieb:
> Guten Tag ...
>
> Hier mein Eigenversuch. Es compiliert und läuft fehlerfrei.
> Zufall oder zufällig richtig ?

Was um alles in der Welt hat das mit diesem Thread zu tun?

Oder mit dem Titel?

Schafft der Durchschittsdepp heute es nicht mal mehr einen Thread zu 
eröffnen, wenn er etwas fragen will? Das wäre für mich eher ein Grund 
für Depressionen.

Es könnte ja sein, daß sich jemand (z.B. der TE oder jemand, der ihm 
schon mal geantwortet hat), NICHT mit deinem Problem beschäftigen will.

von Martin Beuttenmüller (Gast)


Lesenswert?

@ Hrn. Wachtler

Es war keineswegs meine Absicht, einen neuen Tread mit einer
neuen Frage zu eröffnen.
Vielmehr habe ich versucht, das obige Problem durch "trial 'n error"
zu lösen und mir die Freieit genommen, das Ergebnis zu posten.

Mag sein, das mein Auftreten mißverständlich geraten ist -
Ihre Kritik hätte auch etwas dezenter formuliert werden können.

---

Ersetzen wir doch einfach "Genies" durch "Profis".
Ich bin keins von beidem, aber immer gewillt, dazuzulernen ...

In diesem Sinne
Martin

von Kindergärtnerin (Gast)


Lesenswert?

aus
Sir.Tom schrieb:
> void main()
> {
>   struct etest *errstack[3];
>
>   errstack[1]->err_id = 0x55;  // ein element testweise beschreiben.
>
>   printf("id: %x\n",errstack[1]->err_id);

mach mal
1
void main()
2
{
3
  struct etest errstack[3];
4
5
  errstack[1].err_id = 0x55;  // ein element testweise beschreiben.
6
 
7
  printf("id: %x\n",errstack[1].err_id);
8
}

von Heinz L. (ducttape)


Lesenswert?

Der "Fehler" hier ist einfach, dass der Zeiger nicht initialisiert ist.

In C++ zeigt ein Pointer, so er nicht initialisiert wird, einfach 
"irgendwohin". Genau so wie eine Variable einfach "irgendeinen" Wert hat 
wenn sie nicht initialisiert wird. Ein Pointer ist nämlich im Endeffekt 
nix anderes als eine Variable in der Breite des Adressbusses (also 32 
bzw. 64 bit) wo halt eine Zahl drin steht, die dann eben als Adresse 
interpretiert werden soll statt als Zahl. Und da steht nun eben ohne 
Initialisierung irgendeine Adresse drin. Eine Adresse, die mit nahezu 
perfekter Sicherheit NICHT Dir gehört.

Im Debug-Modus werden Variablen häufig standardmäßig mit 0 
initialisiert. Sowas testet man entsprechend mit maximaler Optimierung, 
damit genau sowas vom Compiler eben nicht gemacht wird. Das nebenbei.

Was in Deinem Beispiel passiert ist im Endeffekt das gleiche was hier 
passieren würde mit "normalen" Variablen:
1
int c, d, e, f;
2
printf("%d %d %d %d\n", c, d, e, f);

Jedes Mal wenn Du diesen Code ausführst, wirst Du wahrscheinlich anderes 
angezeigt bekommen. Zumindest wenn Dein Rechner schon 'ne Weile in 
Gebrauch war und entsprechend Datenmüll im Speicher noch rumliegt. Tut 
nicht weh, weil man's eben aufräumen soll bevor man's verwendet.

Bei "normalen" Variablen existieren diese zur Beschreibung bereits wenn 
Du sie deklarierst. Der Aufruf
1
unsigned int c;
2
c = 0x08154711;

ist demnach durchaus zulässig. Was hier passiert ist, dass der Compiler 
im ram nach hinreichend Platz sucht um eine integer Variable anzulegen, 
merkt sich wo der Platz war und weiß, dass er jedesmal, wenn du was von 
der Variablen "c" willst er in diesen Platz schreiben soll. In "c" steht 
jetzt "irgendwas" drin. Wir haben ja auch nix reingetan (noch). Wir 
haben mit "unsigned int c" dem Compiler nur gesagt, er soll uns wo Platz 
für 'ne Int suchen.

Mit der 2. Zeile wird nun in diesen Platz der Wert 0x08154711 
geschrieben. Das funktioniert.

Nun gucken wir doch mal was bei Zeigern passiert.
1
unsigned int *c;
2
*c = 0x08154711;

In der 1. Zeile sagen wir dem Compiler, er soll uns wo Platz für eine 
Adresse suchen. Wir sagen ihm sogar, dass diese Adresse irgendwann mal 
eine unsigned int enthalten wird. Wesentlich dabei ist, dass der Platz 
für den ZEIGER hier reserviert wird. NICHT der Platz für die unsigned 
int auf die er zeigt. Es wird nicht, wie oft behauptet wird, "nichts" 
vom Compiler hier reserviert. Es wird der Platz für den ZEIGER 
reserviert. Nicht, natürlich, der Platz auf den er zeigt. Genauso wie 
"unsigned int c" im 1. Beispiel Müll enthielt bevor wir eine Zahl 
hineinschrieben, genauso enthält "unsigned int *c" eine "Mülladresse" 
bevor wir eine sinnvolle Adresse hineinschreiben. Entsprechend geht 
diese Zuweisung schief, wir wollen hier den Wert 0x08154711 an eine 
Stelle schreiben, die uns nicht gehört!

DAS geht dementsprechend schief und endet mit einem Programmabsturz.


Jetzt können wir entweder eine integervariable reservieren und unser *c 
drauf zeigen lassen.
1
unsigned int c;
2
unsigned int *ptr_c;
3
ptr_c = &c;
4
c = 0x08154711;
5
*ptr_c = 0x08154711; //tut genau das gleiche wie die Zeile darüber

Beachte: Es muss KEIN gültiger Wert an der Adresse von c stehen damit 
wir ihre Adresse dem Zeiger drauf zuweisen können. Was drin steht ist 
dem Zeiger herzlich egal. Wichtig ist für ihn nur, dass diese Adresse 
"gültig" ist und dem Programm "gehört", also allokiert wurde, entweder 
(wie hier) vom Compiler indem es eine Variable dazu gibt oder (wie wir's 
gleich machen werden) vom Programmierer indem er den Speicher dynamisch 
anfordert.

Wenn die Adresse auf die der Zeiger zeigt auf diese Art definiert wird, 
ist es NICHT notwendig sie "manuell" freizugeben, da diese Adresse vom 
compiler allokiert worden ist.

Anders siehts's aus wenn der Speicher dynamisch allokiert wird.
1
unsigned int *ptr_c;
2
3
//C - Code
4
ptr_c = (unsigned int*)malloc(sizeof(unsigned int));
5
6
//Alternativ dazu: C++
7
ptr_c = new int;
8
9
*ptr_c = 0x08154711;
10
//Jetzt damit arbeiten
11
12
//C - Code 
13
free (ptr_c);
14
15
//C++ code
16
delete ptr_c;
17
18
ptr_c = 0; //Muss nicht sein, sorgt aber für weniger Kopfweh.

Speicher den Du explizit allokierst (mit malloc oder new) MUSST Du auch 
wieder durch die dazu passende "Aufräumaktion" deallokieren (mit free 
bzw. delete).

Ist nicht simpel. Ich weiß. Wennst es aber einmal begriffen hast ist's 
echt kein Drama mehr. Ein wenig ASM hilft dabei übrigens sehr.

von Martin Beuttenmüller (Gast)


Lesenswert?

@ Heinz L.

Herzlichen Dank für die ausführliche Erklärung.
Martin

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.