Forum: Compiler & IDEs nested structs und C / C++ -> too many initializers


von Michael Z. (incunabulum)


Lesenswert?

Ich steh mal gerade wieder auf'm Schlauch...

Und zwar geht es um nested structs zur Abbildung eines einfachen Menüs. 
Hierbei habe ich mich an die anderen Threads zu dem Thema gehalten, 
siehe hier: Beitrag "konstante Menustruktur im FLASH ablegen"

Das Menü wird abgebildet durch MyMenu, die einzelnen Menüseiten durch 
MyMenuItem Objekte.
1
typedef struct MyMenuItem {
2
  u08 type; 
3
  const char *text; 
4
};
5
6
typedef struct MyMenu {
7
  const char *caption; 
8
  MyMenuItem items[]; 
9
10
};

Initialisiere ich eine bestimmte Menüstruktur mittels
1
const char PROGMEM menuCaption[] = "MainCaption";
2
const char PROGMEM menuBoolItem[] = "BoolItem";
3
const char PROGMEM menuIntItem[] = "IntItem";
4
5
MyMenuItem boolItem = { 
6
    type: ENTRY_TYPE_ACTION, 
7
    text: menuBoolItem
8
    };
9
MyMenuItem intItem =  {ENTRY_TYPE_INT, menuIntItem};
10
11
MyMenu myMenu = {
12
    caption: menuCaption, 
13
    items: {boolItem, intItem}
14
}

und jage es durch den Compiler (C++), so bekomme ich folgende 
Fehlermeldung:
1
error: too many initializers for 'MyMenuItem [0]'

Nur, was ist daran falsch???

Irgendwo scheine ich hier einen Denkfehler zu haben, der mir nicht 
auffält. g++ unter Linux wie auch der normale gcc mit -std=gnu99 liefern 
alle den gleichen Fehler

cu, Michael

PS: Compiler-Aufruf über
1
avr-gcc -c -mmcu=atmega8 -I. -x c++ -gstabs -DF_CPU=16000000UL 
2
-Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 
3
-fno-exceptions -Wall -Wa,-adhlns=.obj/main.o.lst -I../Avr-LibMz 
4
-ffunction-sections  -MD -MP -MF .dep/main.o.d main.cpp -o .obj/main.o

von Oliver (Gast)


Lesenswert?

Auf den ersten Blick:

typedef struct {...} name;


Oliver

von Michael Z. (incunabulum)


Lesenswert?

Nö, leider auch nicht...

Michael

von yalu (Gast)


Lesenswert?

> typedef struct MyMenu {
>   const char *caption;
>   MyMenuItem items[];
>
> };

Das Array MyMenuItem hat keine Größenangabe und somit die Größe 0.
Weiter unten versuchst du, das Array mit zwei Elementen zu
initialisieren.

Natürlich willst du die Größe des Arrays abhängig von der
Initialisierung machen. Das geht aber anders.

Du ersetzt das Array unbekannter Größer durch einen Pointer:
1
typedef struct {
2
  const char *caption; 
3
  MyMenuItem *items; 
4
} MyMenu;

Die Initialisierung erfolgt in zwei Schritten. Erst legst du ein
initialisiertes Array für die Items an:
1
MyMenuItem items1[] = { boolItem, intItem };

Hier kann man mit leeren [] arbeiten, da die Arraygröße in der selben
Definition durch die Initialisierung gegeben ist.

Nun kannst du myMenu mit dem eben erzeugten Array initialisieren:
1
MyMenu myMenu = {
2
  caption: menuCaption, 
3
  items:   items1
4
};

Das ist zwar etwas umständlich, aber Strukturen müssen in C/C++ eine
feste Größe haben. Ist die Größe eines in einer Struktur enthaltenen
Arrays bei der Strukturdeklaration noch nicht bekannt, muss wie
beschrieben mit Pointern gearbeitet werden.

Nebenbei: Der typedef in den Strukturdeklarationen ist zwar nicht
falsch, aber überflüssig.

von yalu (Gast)


Lesenswert?

Nachtrag: Lass dich nicht verwirren durch die Umstellung der
Strukturdeklaration (Typname ans Ende der Deklaration):
1
typedef struct {
2
  const char *caption; 
3
  MyMenuItem *items; 
4
} MyMenu;

Ich dachte erst, das sei ein C-Programm. In C++ ist auch deine
Schreibweise richtig. Und, wie gesagt, der typedef ist überflüssig.
Die übliche C++-Schreibweise ist so:
1
struct MyMenu {
2
  const char *caption; 
3
  MyMenuItem *items; 
4
};

von Michael Z. (incunabulum)


Lesenswert?

Klatsch! So landet die Hand an der Stirn...

Yalu, Danke dir. Dass C/C++ nicht in der Lage ist, aus implizit 
definierten Arrays die Größe zu ziehen, darauf bin ich wirklich nicht 
gekommen.

Und den typedef hatte ich nur aus verzweifelten Testgründen drin. In der 
Orginalversion war das so, wie du im letzten Post zeigst, geschrieben.

Denn kann ich hoffen, dass morgen alles auch in Echt klappt, wie ich es 
mir vorstelle :-)

cu, Michael

von Rolf Magnus (Gast)


Lesenswert?

> Yalu, Danke dir. Dass C/C++

Es gibt keine Sprache "C/C++". Es gibt C, und es gibt C++. Such dir eins 
davon aus.

> nicht in der Lage ist, aus implizit definierten Arrays die Größe zu
> ziehen, darauf bin ich wirklich nicht gekommen.

Das liegt daran, daß es in C++ keine "implizit definierten Arrays" gibt. 
Ein Array hat immer eine Größe. Was sollte deiner Meinung nach
sizeof(MyMenu) zurückgeben, wenn die Struktur ein Array unbekannter 
Größe enthalten könnte? Wie würden Arrays aus diesen Strukturen 
funktionieren?

von yalu (Gast)


Lesenswert?

> Ein Array hat immer eine Größe.

Fast immer. Es gibt die so genannten "flexible array members", die
genau so deklariert werden, wie es Michael gemacht hat, also als
letztes Element einer Struktur und mit leeren []. Diese Arrays können
aber nicht direkt initialisiert werden und werden üblicherweise nur in
dynamisch allozierten Strukturen verwendet, bei denen die Größe des
Speicherobjekts größer als die Größe der Struktur ist.

> Was sollte deiner Meinung nach sizeof(MyMenu) zurückgeben, wenn die
> Struktur ein Array unbekannter Größe enthalten könnte?

Sizeof liefert die Größe der Struktur ohne das Array plus die Anzahl
der Pad-Bytes die ggf. vor dem Array eingefügt werden.

von Michael Z. (incunabulum)


Lesenswert?

Rolf Magnus wrote:
> Es gibt keine Sprache "C/C++". Es gibt C, und es gibt C++. Such dir eins
> davon aus.

Ich sehe C++ immer noch als Erweiterung bzw. Add-On zu C. Alles, was ich 
in C machen kann, dass geht auch in C++. Insofern sind die Sprachen für 
mich nicht vollständig zu trennen.

Desweiteren hatte ich es aus Trotz auch noch unter normalem C probiert.

> Das liegt daran, daß es in C++ keine "implizit definierten Arrays" gibt.
> Ein Array hat immer eine Größe.

MyMenu myMenu = {
    caption: menuCaption,
    items: {boolItem, intItem}
}

Und hier? Zumindest für mich als denkenden Mensch ergibt sich hier, dass 
items ein Array mit 2 Einträgen ist. Der Compiler erkennt dies aber 
nicht. Daher mein eventuell ungenaues "implizit definiertes" Array.

cu, Michael, der vielleicht von modernen Sprachen verwöhnt und verzogen 
ist. Da geht so was nämlich alles.

von Oliver (Gast)


Lesenswert?

>Und hier? Zumindest für mich als denkenden Mensch ergibt sich hier, dass
>items ein Array mit 2 Einträgen ist.

Für C-Compiler auch (C ist eben keine Untermenge von C++). Nur scheitert 
das dann daran, daß C nur konstante Initializer zulässt - und boolItem, 
intItem, sind keine.

Oliver

von Rolf Magnus (Gast)


Lesenswert?

> Ich sehe C++ immer noch als Erweiterung bzw. Add-On zu C. Alles, was
> ich in C machen kann, dass geht auch in C++.

Ja. Man muß es halt etwas anders schreiben. Allerdings gilt die Aussage 
für die meisten Kombinationen von Programmiersprachen.

> Insofern sind die Sprachen für mich nicht vollständig zu trennen.

Ich finde eine Trennung notwendig. Man sollte entweder richtig C oder 
richtig C++ programmieren und nach Möglichkeit nicht einen Mischmasch, 
der eigentlich eher C ist, aber ab und zu ein paar C++-Elemente 
eingestreut hat.

> Das liegt daran, daß es in C++ keine "implizit definierten Arrays"
> gibt.
> Ein Array hat immer eine Größe.

> MyMenu myMenu = {
>     caption: menuCaption,
>     items: {boolItem, intItem}
> }
>
> Und hier? Zumindest für mich als denkenden Mensch ergibt sich hier,
> dass items ein Array mit 2 Einträgen ist.

Für mich ergibt sich, daß items wohl in der Definition von MyMenu als 
Array oder Struct mit mindestens zwei Elementen definiert worden sein 
muß. Wurde es aber nicht. Also können diese nicht existenten zwei 
Elemente auch nicht initialisiert werden.
Eine struct-Definition gibt an, wie alle Instanzen dieses Typs aussehen. 
Da kann man nicht einfach Teile dieser Information weglassen und dann 
nachher alle Instanzen verschieden machen
Nochmal: Was sollte sizeof(MyMenu) ausgeben? Stell dir mal ein Array aus 
MyMenu vor. Wie soll die Indizierung funktionieren, wenn die Größe der 
Elemente unbekannt ist? Wie soll eine dynamische Allokierung mit malloc 
funktionieren?

> Der Compiler erkennt dies aber nicht.

Er erkennt, daß du nicht-existente Array-Elemente zu initialisieren 
versuchst.

> Daher mein eventuell ungenaues "implizit definiertes" Array.

Ich habe schon verstanden, wie du das meintest, aber das geht in C nun 
mal nicht.

> cu, Michael, der vielleicht von modernen Sprachen verwöhnt und verzogen
> ist. Da geht so was nämlich alles.

Da wird aber auch nur mit Wasser gekocht. Das was die machen, kannst du 
auch selbst machen, indem du statt ein Array einen Zeiger verwendest und 
den dann auf ein dynamisch angelegtes Array zeigen läßt. Die "modernen" 
Sprachen machen das halt im Hintergrund für dich. Die haben allerdings 
auch nahezu unbegrenzt Speicher zur Verfügung.

von Michael Z. (incunabulum)


Lesenswert?

Rolf Magnus wrote:
> Ich finde eine Trennung notwendig. Man sollte entweder richtig C oder
> richtig C++ programmieren und nach Möglichkeit nicht einen Mischmasch,
> der eigentlich eher C ist, aber ab und zu ein paar C++-Elemente
> eingestreut hat.

Ich denke, hier muss jeder seinen eigenen persönlichen Stil finden. Gut 
zu sehen z. B. by Python. Hier werden selbst von den Entwicklern 
methodenorientieres und objektorientiertes Programmieren vermicht. "Use 
the best tool for the job".

>> der vielleicht von modernen Sprachen verwöhnt und verzogen
>> ist. Da geht so was nämlich alles.
>
> Da wird aber auch nur mit Wasser gekocht. Das was die machen, kannst du
> auch selbst machen, indem du statt ein Array einen Zeiger verwendest und
> den dann auf ein dynamisch angelegtes Array zeigen läßt. Die "modernen"
> Sprachen machen das halt im Hintergrund für dich. Die haben allerdings
> auch nahezu unbegrenzt Speicher zur Verfügung.

Hab wohl noch ein bisschen zu lernen in dieser Hinsicht... so siehts 
aus. Bei neueren Sprachen gibt es malloc etc. Geschichten nicht. Und 
daran hab ich jetzt zu knabbern :-)

cu, Michael, der auch lieber QT als die std:: verwendet, wenn es schon 
um C++ geht

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.