Forum: Compiler & IDEs 2 structs und gegenseitige Referenzierung mit Pointer


von Markus (Gast)


Lesenswert?

Hallo

Ich möchte zwei structs definieren, die jeweils ein Pointer als Element 
enthalten um gegenseitig auf sich verweisen zu können. Also so:
1
typedef struct BOX {
2
  char *text;
3
  struct BOX_ITEM *boxItem;
4
};
5
6
typedef struct BOX_ITEM {
7
  uint8_t value;
8
  struct BOX *box ;
9
};
Dabei erhalte ich schon mal eine Warnung (jedoch nur beim 1. 
Compilieren, folgende Compilationen ohne Änderung des Codes bringen 
keine Warnungen mehr):
../box_test.c:29: warning: useless storage class specifier in empty 
declaration
Weshalb gibts die Warnung nur beim ersten Mal?

Wie kann ich nun je eine Instanz erzeugen, da
1
char mytext[] = "blabla"; // (1)
2
struct BOX box1 = {mytext, &box1Item1}; // (2)
3
4
struct BOX_ITEM box1Item1 = {0, &box1}; // (3)
ein Fehler erzeugt, da der Compiler zum Zeitpunkt (2) box1item1 ja noch 
gar nicht kennt.
Wie muss ich das Problem lösen?
Was muss ich dazu in die header-Datei eintragen?

Vielen Dank.

von Rolf M. (rmagnus)


Lesenswert?

Markus schrieb:
> Dabei erhalte ich schon mal eine Warnung (jedoch nur beim 1.
> Compilieren, folgende Compilationen ohne Änderung des Codes bringen
> keine Warnungen mehr):
> ../box_test.c:29: warning: useless storage class specifier in empty
> declaration

> Weshalb gibts die Warnung nur beim ersten Mal?

Vermutlich wird die Datei nur beim ersten mal compiliert. Warum sollte 
das auch nochmal getan werden, wenn du die zwischendrin nicht änderst?

> Wie muss ich das Problem lösen?

Per "forward declaration":

extern struct BOX_ITEM box1Item1;

struct BOX box1 = {mytext, &box1Item1};
struct BOX_ITEM box1Item1 = {0, &box1};

> Was muss ich dazu in die header-Datei eintragen?

Die extern-Deklaration.

von AND (Gast)


Lesenswert?

Markus schrieb:
> typedef struct BOX {
>   char *text;
>   struct BOX_ITEM *boxItem;
> };
>
> typedef struct BOX_ITEM {
>   uint8_t value;
>   struct BOX *box ;
> };


struct BOX {
...
};

oder

typedef struct {
...
} BOX;

Je nachdem ob man beim Anlegen von Variablen "struct BOX b;" oder 
einfach "BOX b;" schreiben will.

von XTerminator (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Die extern-Deklaration.

Warum um alles in der Welt extern? Sinple forward declaration reicht.

von Markus (Gast)


Lesenswert?

ok danke, das klappt schon mal soweit. Doch weshalb brauche ich bei der 
Instanzierung noch das keyword struct bei
1
struct BOX_ITEM box1Item1 = {0, &box1}; // (3)
?

Ich mach ja bei der Definition ein typedef, deshalb sollte es doch auch 
ohne "struct" funktionieren?!? Falls ich es (sowohl im .c als auch im 
.h-File) weglasse, erscheint die Meldung:
../box_test.c:34: error: expected '=', ',', ';', 'asm' or 
'__attribute__' before 'box1Item1'

Wie kommt das?

von Markus (Gast)


Lesenswert?

... ich hab soeben noch ein weiteres Problem:
Wieso lässt sich der Wert eines Elements im struct nicht neuzuweisen?
1
char mytext[] = "blabla"; // (1)
2
char secondtext[] = "blabla"; // (2)
3
struct BOX box1 = {mytext, &box1Item1}; // (3)
4
box1.text = second; // (4)

Zeile 4 ruft folgenden Fehler hervor:
../box_test.c:39: error: expected '=', ',', ';', 'asm' or 
'__attribute__' before '.' token

Was bedeutet das?
Vielen Dank für eure Hilfe!

von Noname (Gast)


Lesenswert?

In der Syntax von C gibt es dedizierte Positionen für die Namen von 
structs und von typedefs.
1
typedef struct <struct_name> {
2
blabla
3
} <typname>;
4
5
<typname> VarableX;

Du kannst auch structs deklarieren ohne deswegen einen neuen Typ zu 
definieren.
1
struct <struct_name> {
2
blabla
3
};
4
5
struct <struct_name> VariableX;

Du kannst auch den struct namen weglassen wenn Du einen Typen 
definierst.
Eine sog. anonyme Struktur.
1
typedef struct {
2
blabla
3
} <typename>;
4
<typname> VariableX;
Aber was Du nicht kannst, ist den typnamen weglassen und stattdessen den 
struct-namen als Typnamen verwenden, wie Du es getan hast.
1
typedef struct <struct_name> {
2
blabla
3
};
4
<typname> VariableX;    // geht nicht, weil kein name für den Typ angegeben
5
struct <struct-name> VariableY; // geht, weil der struct name dennoch bekannt ist.
Wobei ich bei dem letzten Code zumindest eine Warnung erwarten würde, 
denn typdefinitionen ohne Typnamen sind eigentlich sinnlos.

Du kannst also tpynamen und struct namen nicht in der gleichen Weise 
verwenden.
struct <struct-name> definiert eben keinen Typen. Deswegen musst Du 
ausdrücklich struct <struct-name> schreiben, wenn Du eine Variable mit 
der Struktur als ihrem Typ anlegen willst.

Lies mal in einem C-Buch nach.

von DirkB (Gast)


Lesenswert?

In welchem Kontext steht denn Zeile 4?

Zuweisungen sind nur innerhalb von Funktionen möglich.

von Noname (Gast)


Lesenswert?

>...wenn Du eine Variable mit der Struktur als ihrem Typ anlegen willst.
Ist eigentlich nicht korrekt ausgedrückt.

Ein Strukturname identifiziert keinen Typ sondern eben eine Struktur.
Anders ausgedrückt kann man "struct" resp. "union" als ein Typ wie int 
oder char ansehen, er bedarf aber eines Namens, eben der gewollten 
"struct" oder "union" um an Stelle eines Typs stehen zu können, also ein 
Typ "zu sein".

von (prx) A. K. (prx)


Lesenswert?

Da Struct-Namen einen eigenen Namespace besitzen kann man "struct XXX" 
und einen Typedefname XXX auch kombinieren:
1
typedef struct BOX {
2
  char *text;
3
  struct BOX_ITEM *boxItem; // nicht: BOX_ITEM *boxItem;
4
} BOX;
5
6
typedef struct BOX_ITEM {
7
  uint8_t value;
8
  struct BOX *box ; // oder auch: BOX *box;
9
} BOX_ITEM;
Man kann dann überall dort die Form "struct XXX" verwenden, wo diese 
Struct noch nicht bekannt ist und hat dennoch die Möglichkeit, im Rest 
vom Code den Typedef-Name benutzen zu können. Mit dem gleichen Namen.

Man kann alternativ auch den Typedef vorziehen und die Struct 
nachreichen:
1
typedef struct BOX_ITEM BOX_ITEM;
2
typedef struct BOX BOX;
3
4
struct BOX {
5
  char *text;
6
  pBOX_ITEM boxItem;
7
};
8
9
struct BOX_ITEM {
10
  uint8_t value;
11
  pBOX box;
12
};

von (prx) A. K. (prx)


Lesenswert?

Oops, so wars gemeint:
1
typedef struct BOX_ITEM BOX_ITEM;
2
typedef struct BOX BOX;
3
4
struct BOX {
5
  char *text;
6
  BOX_ITEM *boxItem;
7
};
8
9
struct BOX_ITEM {
10
  uint8_t value;
11
  BOX *box;
12
};
oder kürzer
1
typedef struct BOX_ITEM BOX_ITEM;
2
3
typedef struct BOX {
4
  char *text;
5
  BOX_ITEM *boxItem;
6
} BOX;
7
8
struct BOX_ITEM {
9
  uint8_t value;
10
  BOX *box;
11
};

von Oliver (Gast)


Lesenswert?

Markus schrieb:
> Weshalb gibts die Warnung nur beim ersten Mal?

Weil das nur eine Warnung ist, und die c-Datei trotzdem kompiliert 
wurde. Da make nur geänderte Files neu kompiliert, bekommt der Compiler 
die Datei bei späteren build-Vorgängen gar nicht mehr zu sehen, damit 
gibt es da auch keine Warnung mehr.

Willst du das nicht, hilft -WError. Dann wird jede Warnung als Fehler 
behandelt.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

XTerminator schrieb:
> Rolf Magnus schrieb:
>> Die extern-Deklaration.
>
> Warum um alles in der Welt extern? Sinple forward declaration reicht.

Weil man bei globalen Variablen eine "simple forward declaration" macht, 
indem man extern davor schreibt.

A. K. schrieb:
> Man kann alternativ auch den Typedef vorziehen und die Struct
> nachreichen:

Aber auch nur weil
1
typedef struct BOX_ITEM BOX_ITEM;
gleich noch eine implizite Deklaration von struct BOX_ITEM beinhaltet.

von (prx) A. K. (prx)


Lesenswert?

Klar. Aber das ist die Vorzugslösung für Leute, die sich mit dem 
archaischen "struct XXX" aus grauer Vorzeit möglichst wenig befassen 
wollen. Man hat damit eine forward declaration vom typedef durchgeführt 
und kann bei den gewohnteren typedefs bleiben.

von Markus (Gast)


Lesenswert?

Vielen Dank für die Inputs, hat mir sehr geholfen.

LG
Markus

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.