Forum: PC-Programmierung Probleme bei Pointer in C. Code crasht


von marcel (Gast)


Lesenswert?

Hallo,
ich habe hier einen Code, welcher immer abstürzt
1
static uint32 table_p[64];
2
static table_T *resp_table;
3
4
typedef struct
5
{
6
  uint8 inUse;
7
  uint8 status;
8
  ...
9
} table_T, *table_t;
10
11
typedef struct
12
{
13
...
14
uint32 *table;
15
uint32 size;
16
...
17
}init_ts
18
19
20
void init(void)
21
{
22
23
  init_ts init_s;
24
...
25
  init_s.table = table_p;
26
  init_s.size= 64;
27
...
28
}
29
30
31
static void install_tab(init_ts *init_s)
32
{
33
  resp_table = (table_t)(init_s->table);
34
  for(uint32 n=0; n<init_s->size; n++)
35
  {
36
    resp_table[n].inUse = 0;  // hier crasht es. Im Debugger (qt-creator) sehe ich aber, dass die Adresse von resp_table = die Adresse von table_p ist 
37
    resp_table[n].status = 0;
38
  }
39
}

Im Debugger steht
table_p    @0x6ae0e0       [uint32]64
resp_table 0x6ae0e00000000 table_T*

resp_table liegt an der Adresse 0x6a21d0, beinhaltet aber den oben 
beschriebenen Wert.

Als Fehler bekomme ich segmentation fault. Was ja heißt, dass in einem 
falschen Bereich geschrieben wurde.

Woran kann das liegen?

von B. W. (yesitsme)


Lesenswert?

Hmm... das compiliert nicht...

marcel schrieb:
> Als Fehler bekomme ich segmentation fault. Was ja heißt, dass in einem
> falschen Bereich geschrieben wurde.

Schau mal in Zeile 42. Da ist der Übeltäter.

von Marco H. (damarco)


Lesenswert?

Jup du versuchst eine Struktur auf einen uint32 Pointer zu drücken. 
Deine Struktur ist im Stack aber nicht zusammenhängend angelegt. Du 
greifst unerlaubt in den Speicher..

Dein Cast ist falsch und das ganze macht so gesehen auch keinen Sinn.... 
Der Code ist unübersichtlich und was eigentlich passieren soll ist 
unklar.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

B. W. schrieb:
> Hmm... das compiliert nicht...

Der ziemlich zusammengestückelte und fehlerhafte Code lässt auch darauf 
schießen, dass das nicht der Code ist, der das beobachtete Problem 
verursacht hat. So kann man damit wenig anfangen. Besser wäre ein 
minimales, aber vollständiges Programm, das du genau so ausgeführt 
hast und das den Fehler zeigt.

Marco H. schrieb:
> Dein Cast ist falsch und das ganze macht so gesehen auch keinen Sinn....

Dachte ich zuerst auch, aber ist er nicht. Man achte beim typedef 
darauf, dass die Struktur selbst table_T mit großem T heißt, während 
table_t einen Zeiger darauf bezeichnet. Ziemlich heimtückische Falle.
Meine Glaskugel meint, es könnte sein, dass ein table_T größer ist als 
ein uint32, und daher in ein Array aus 64 uint32 keine 64 table_T 
reinpassen.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> Dachte ich zuerst auch, aber ist er nicht. Man achte beim typedef
> darauf, dass die Struktur selbst table_T mit großem T heißt, während
> table_t einen Zeiger darauf bezeichnet. Ziemlich heimtückische Falle.
> Meine Glaskugel meint, es könnte sein, dass ein table_T größer ist als
> ein uint32, und daher in ein Array aus 64 uint32 keine 64 table_T
> reinpassen.

Das ist relativ egal, da es eh undefined behavior ist (verstößt gegen 
die "strict aliasing" Regeln)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wie schon geschrieben wurde, gibt es einen Array-Überlauf, wenn
sizeof(table_T) > sizeof(uint32) ist.

Die Zeile

1
  resp_table = (table_t)(init_s->table);

verstößt gegen die strict aliasing Rule, d.h. selbst wenn es keinen
Array-Überlauf gibt, ist das Verhalten dennoch undefiniert.

Es ist nicht klar, ob init_s in dieser Zeile überhaupt initialisiert
ist. Es gibt zwar die Funktion init(), die evtl. auch irgendwo
aufgerufen wird, aber darin wird sinnloserweise nur eine lokale Variable
initialisiert.

von mh (Gast)


Lesenswert?

Yalu X. schrieb:
> Die Zeile
>
>   resp_table = (table_t)(init_s->table);
>
> verstößt gegen die strict aliasing Rule, d.h. selbst wenn es keinen
> Array-Überlauf gibt, ist das Verhalten dennoch undefiniert.

Genau genommen sorgt erst die Zeile
1
resp_table[n].inUse = 0;
für ub ;-) Die Konvertierung des Pointers ist erlaubt, der Zugriff 
nicht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Genau genommen sorgt erst die Zeile
>  resp_table[n].inUse = 0;
> für ub ;-) Die Konvertierung des Pointers ist erlaubt, der Zugriff
> nicht.

Richtig, die Pointer-Konvertierung war nur die Vorbereitung zum Unheil.

von Quadrat (Gast)


Lesenswert?

marcel schrieb:
> static table_T *resp_table;
>
> typedef struct
> {
>   uint8 inUse;
>   uint8 status;
>   ...
> } table_T, *table_t;

Kann man überhaupt eine Variable vom Typ table_T erzeugen bevor sie 
überhaupt definiert wurde?  Das ist doch auch falsch.

von leo (Gast)


Lesenswert?

marcel schrieb:
> void init(void)
> {
>
>   init_ts init_s;

Du initialisierst hier eine lokale Variable, die dann wieder 
verschwindet. Dann verwendest du einen Pointer auf diese. Das geht 
nicht.

BTW ein compilierendes Beispiel mit Warnings wuerde helfen.

leo

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

leo schrieb:
> Du initialisierst hier eine lokale Variable, die dann wieder
> verschwindet.

Korrekt.

> Dann verwendest du einen Pointer auf diese.

Nein, es wird ein Pointer auf eine globale Variable sein, die 
zufälligerweise genauso heisst. Sonst würde das gar nicht durch den 
Compiler gehen.

von mh (Gast)


Lesenswert?

Frank M. schrieb:
> leo schrieb:
>> Du initialisierst hier eine lokale Variable, die dann wieder
>> verschwindet.
>
> Korrekt.
>
>> Dann verwendest du einen Pointer auf diese.
>
> Nein, es wird ein Pointer auf eine globale Variable sein, die
> zufälligerweise genauso heisst. Sonst würde das gar nicht durch den
> Compiler gehen.

Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine 
lokale Variable init_s. Die Funktion install_tab hat einen Parameter 
init_s. Da sehe ich keine Probleme. Ok es ist unklar, wann welche 
Funktion aufgerufen wird, wann das Programm abstürzt und welchen Zustand 
die debug-Ausgabe abbildet. Aber so wie es aussieht gehe ich von 
folgendem Ablauf aus:
init erzeugt ein init_ts mit Namen init_s und "initialisiert" es mit 
einem Pointer auf das globale table_p. Weiterhin in init wird dann 
install_tab mit einem Pointer auf das lokale init_s aufgerufen. 
install_tab schreibt dann über den Pointer im init_s Parameter in das 
globale table_p.
Das sollte funktionieren, wenn man das undefined behavior beseitigt.

von Lothar (Gast)


Lesenswert?

Marco H. schrieb:
> Deine Struktur ist im Stack aber nicht zusammenhängend angelegt

PC oder uC?

__packed struct

https://www.keil.com/support/man/docs/armcc/armcc_chr1359124968737.htm

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine
> lokale Variable init_s. Die Funktion install_tab hat einen Parameter
> init_s.

Die eine Variable init_s hat aber mit der anderen init_s - wo auch immer 
die herkommt - nichts zu tun. An die lokale Variable kommt man von 
außerhalb nicht dran, darum kümmert sich der Compiler. Oder TO hat 
seinen Code soweit zurechtgestutzt, dass der präsentierte Code nichts 
mehr mit dem tatsächlichen Code zu tun hat.

von Marco H. (damarco)


Lesenswert?

Naja ganz weg ist sie ja nicht, sie ist im Stack noch vorhanden. Bis sie 
überschrieben wird. Den Pointer von der Struktur init_ts die der 
Funktion install_tab() übergeben wird muss ja wo her kommen?

von mh (Gast)


Lesenswert?

Frank M. schrieb:
> mh schrieb:
>> Ich verstehe nicht so ganz worauf ihr euch bezieht. In init gibt es eine
>> lokale Variable init_s. Die Funktion install_tab hat einen Parameter
>> init_s.
>
> Die eine Variable init_s hat aber mit der anderen init_s - wo auch immer
> die herkommt - nichts zu tun. An die lokale Variable kommt man von
> außerhalb nicht dran, darum kümmert sich der Compiler. Oder TO hat
> seinen Code soweit zurechtgestutzt, dass der präsentierte Code nichts
> mehr mit dem tatsächlichen Code zu tun hat.

Natürlich hat er ihn zusammengestutzt. Deswegen die ganzen "...". 
Ansonsten würde es ja auch keine Probleme geben, weil init und 
install_tab nicht aufgerufen werden. install_tab wird IN init 
aufgerufen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> install_tab wird IN init aufgerufen.

Ah, das könnte des Rätsels Lösung sein. Danke für den Hinweis. Das zeigt 
aber mal wieder, dass ein nicht-übersetzbarer Code einfach nichts taugt.

von leo (Gast)


Lesenswert?

Frank M. schrieb:
>> Dann verwendest du einen Pointer auf diese.
>
> Nein, es wird ein Pointer auf eine globale Variable sein,

Ja eh, aber das geht aus dem gezeigten Code-Geschwafel nicht so klar 
hervor.

leo

von Marco H. (damarco)


Lesenswert?

Lothar schrieb:
> Marco H. schrieb:
>> Deine Struktur ist im Stack aber nicht zusammenhängend angelegt
>
> PC oder uC?
>
> __packed struct
>
> https://www.keil.com/support/man/docs/armcc/armcc_chr1359124968737.htm

Klar kann man den Compiler anweisen die Struktur so auf dem Stack zu 
packen. Sehr beliebt bei AVRs weil es so schön einfach ist, mit OS 
sollte man dies aber vermeiden.

Gerade Anfängern ist die Tragweite nicht bewusst und dann schlägt man so 
etwas erstmal nicht vor. Da es das eigentliche Problem nicht behebt.

: Bearbeitet durch User
von Lothar (Gast)


Lesenswert?

Marco H. schrieb:
> Compiler anweisen die Struktur so auf dem Stack

Mit OS sollte es doch mit union gehen. Habe hier einen Message Block mit 
256 Byte der als Byte Pointer übergeben werden muss, aber auch Variablen 
enthält, auf die direkt zugegriffen werden kann.

> Sehr beliebt bei AVR

Bei AVR sind eher nicht-aligned struct beliebt z.B. bei Ethernet oder SD 
Treiber. Portierung auf Cortex-M gibt dann erst mal Hardfault.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Die Anfrage mutet ein wenig wie folgende Aufgabe an:

Setze den unvollständigen Haufen einzelner Scherben zur ursprünglichen
Vase zusammen und finde anschließend die Undichtigkeit in derselben ;-)

von Dirk B. (dirkb2)


Lesenswert?

Bei welchem n kommt der Seg-Fault?

von Mark B. (markbrandis)


Lesenswert?

Yalu X. schrieb:
> Die Anfrage mutet ein wenig wie folgende Aufgabe an:
>
> Setze den unvollständigen Haufen einzelner Scherben zur ursprünglichen
> Vase zusammen und finde anschließend die Undichtigkeit in derselben ;-)

Wie immer halt. ;-)

Da sich marcel leider nicht mehr meldet, werden wir wohl kein 
kompilierbares Beispiel mehr erhalten... :-(

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.