Forum: Compiler & IDEs Grösse Array im Struct


von Reiner W. (reiner_w)


Lesenswert?

Steh grad wieder voll auf dem Pointer-Schlauch ;-(

ich zimmer mir grad ein kleines Menü zusammen.
Ich weis nicht, wie die die Größe des Array menuItems[] im struct 
menuItem ermitteln kann.
1
//menu.h
2
3
#ifndef menu_h
4
#define menu_h
5
6
typedef struct menuItem {
7
    const char *text;
8
    void (*itemFunction)(void);
9
    uint8 keep;
10
} MENUMEMBER_t;
11
12
13
typedef struct menu {
14
    const char *text;
15
    MENUMEMBER_t menuItems[];
16
} MENU_t;
17
18
19
void menuAction (void);
20
21
#endif
1
//menu.c
2
#include <stdio.h>
3
#include "menu.h"
4
5
MENU_t mainMenu = {
6
    "Main Menu",       //Menu Name
7
    {                  //Menu Entries
8
        {"MenuItem 1", NULL, 0},  //text, function, keep
9
        {"MenuItem 2", NULL, 1},
10
        {"MenuItem 3", NULL, 1},
11
        {"zurueck",    NULL, 0},
12
    }
13
};
14
15
16
void menuAction (void) {
17
    
18
    printf("keep %d \n",mainMenu.menuItems[0].keep);
19
    printf("text %s \n",mainMenu.menuItems[0].text);
20
    printf("keep %d \n",mainMenu.menuItems[1].keep);
21
    printf("text %s \n",mainMenu.menuItems[1].text);
22
    printf("keep %d \n",mainMenu.menuItems[2].keep);
23
    printf("text %s \n",mainMenu.menuItems[2].text);
24
    printf("keep %d \n",mainMenu.menuItems[3].keep);
25
    printf("text %s \n",mainMenu.menuItems[3].text);
26
   
27
    //size of mainmenu.menuItems
28
    printf("sizeof(mainMenu %lu \n", sizeof(mainMenu.menuItems) / sizeof(mainMenu.menuItems[0]) ) ;  
29
    //<-- Error 'sizeof' to an incomplete type MANUMEMBER_t []
30
       
31
}

Kann mir einer auf die Sprünge helfen ?
Danke
Reiner

von Karl H. (kbuchegg)


Lesenswert?

Reiner W. schrieb:

> ich zimmer mir grad ein kleines Menü zusammen.
> Ich weis nicht, wie die die Größe des Array menuItems[] im struct
> menuItem ermitteln kann.

gar nicht.
Du müsstest dir einen Member in die Struktur machen, in den du bei der 
Initialisierung den Wert reinschreibst.

Ihr erwartet alle zuviel von C. In C ist praktisch nichts irgendwie 
dynamisch. Auch wenn die Strukturdefinition erst mal so aussieht, als ob 
das so wäre
1
typedef struct menu {
2
    const char *text;
3
    MENUMEMBER_t menuItems[];
4
} MENU_t;

Das ist eine 'Mogelpackung'. Wenn du sowas hast, dann bist du immer auf 
dich alleine gestellt. Der Compiler kann dir da nicht weiter helfen. 
Auch nicht mit einem sizeof. Nur durch Betrachten dieser 
Strukturdefinition kann niemand sagen, wieviel Speicher tatsächlich für 
das Array allokiert wurde. Wenn du die Information brauchst, dann musst 
du selber sie dir in die Struktur reinschreiben.
1
typedef struct menu {
2
    const char *text;
3
    uint8_t     nrItems;
4
    MENUMEMBER_t menuItems[];
5
} MENU_t;

wodurch diese ganze dynamische Allokierung
1
MENU_t mainMenu = {
2
    "Main Menu",       //Menu Name
3
     4,                // Anzahl Entries
4
    {                  //Menu Entries
5
        {"MenuItem 1", NULL, 0},  //text, function, keep
6
        {"MenuItem 2", NULL, 1},
7
        {"MenuItem 3", NULL, 1},
8
        {"zurueck",    NULL, 0},
9
    }
10
};

deutlich an Eleganz verliert, weil du eine Abhängigkeit 2-er Member 
hast.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?


von Reiner W. (reiner_w)


Lesenswert?

Karl Heinz schrieb:
> gar nicht.
> Du müsstest dir einen Member in die Struktur machen, in den du bei der
> Initialisierung den Wert reinschreibst.

Danke, für die schnelle Antwort. Ich hatte es befürchtet;-(
Hab vorher so gelöst:
1
...
2
typedef struct menu {
3
    const char *text;
4
    MENUMEMBER_t *menuItems[];
5
} MENU_t;
6
...
7
8
menu.menuItems[0] = &menuItem0;
9
menu.menuItems[1] = &menuItem1;
10
menu.menuItems[2] = &menuItem2;
11
...

Da kann ich dann die Größe ermitteln mit
sizeof(menu.menuItems)/sizeof(menu.menuItems[0]) ermitteln.

Was ist denn sinnvoller ?
(habs grad gesehen, das Problem liegt nicht daran, wie ich das Array am 
struct festmache, sondern daran, dass das Array ohne Länge deklariert 
ist)


Reiner

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Reiner W. schrieb:

> Was ist denn sinnvoller ?

Sinnvoll wäre es zb, wenn du nicht darauf bestehst, alles in einer 
Struktur zu haben
1
typedef struct menu {
2
    const char *text;
3
    uint8_t     nrItems;
4
    MENUMEMBER_t *menuItems;
5
} MENU_t;
6
7
MENUMEMBER_t mainMenuItems[] =
8
{
9
  {"MenuItem 1", NULL, 0},  //text, function, keep
10
  {"MenuItem 2", NULL, 1},
11
  {"MenuItem 3", NULL, 1},
12
  {"zurueck",    NULL, 0},
13
};
14
15
MENU_t mainMenu = {
16
    "Main Menu",       //Menu Name
17
     sizeof( mainMenuItems ) / sizeof( *mainMenuItems ),
18
     mainMenuItems;
19
};

da kann dir jetzt wieder der Compiler unter die Arm greifen und per 
sizeof die Länge des Arrays ermitteln und in die Struktur eintragen.
Du hast halt einen Pointer mehr im System, mit dem der Zusammenhang der 
Menu Beschreibung zu den Items des Menüs hergestellt wird
1
  mainMenu
2
  +-------------------+
3
  | "Main Menu"       |
4
  +-------------------+
5
  | 4                 |
6
  +-------------------+                +--------------------+
7
  |        o-------------------------->| MenuItem 1         |
8
  +-------------------+                | NULL               |
9
                                       | 0                  |
10
                                       +--------------------+
11
                                       | MenuItem 2         |
12
                                       | NULL               |
13
                                       | 1                  |
14
                                       +--------------------+
15
                                       | MenuItem 3         |
16
                                       | ....

: Bearbeitet durch User
von Reiner W. (reiner_w)


Lesenswert?

Karl Heinz schrieb:
> Du hast halt einen Pointer mehr im System, mit dem der Zusammenhang der
> Menu Beschreibung zu den Items des Menüs hergestellt wird

Danke Karl Heinz für die (wie immer) ausführliche Erklärung!
Hast du eigentlich mal ein Einsteigerbuch geschrieben? Würde ich sofort 
kaufen.

Reiner

von Mark B. (markbrandis)


Lesenswert?

Reiner W. schrieb:
> Danke Karl Heinz für die (wie immer) ausführliche Erklärung!
> Hast du eigentlich mal ein Einsteigerbuch geschrieben?

Ja aber hallo! Gleich mal ans Werk machen. :-)

von Karl H. (kbuchegg)


Lesenswert?

Ist mir zu viel Stress :-)

von Udo S. (urschmitt)


Lesenswert?

Karl Heinz schrieb:
> Ist mir zu viel Stress :-)

Musst doch nur ein paar hundert Threadbeiträge hier zusammenkopieren :-)

Zum Thema: Man könnte auch dafür sorgen, dass das Array der MENUMEMBER_t 
immer mit einem Null Wert abgeschlossen ist.
Dann kann man durchiterieren, bzw die Länge ermitteln wie es strlen() 
bei einem String macht.

von Reiner W. (reiner_w)


Lesenswert?

Udo Schmitt schrieb:
> Zum Thema: Man könnte auch dafür sorgen, dass das Array der MENUMEMBER_t
> immer mit einem Null Wert abgeschlossen ist.
> Dann kann man durchiterieren, bzw die Länge ermitteln wie es strlen()
> bei einem String macht.

Stimmt, das hab ich auch schon mal gemacht.
Ich hab jetzt .numberOfItems im Struct zugefügt.
Da ich ja die Menüeinträge sowieso von Hand im Struct anlege, kommt es 
da auch nicht mehr drauf an.

von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:

> Zum Thema: Man könnte auch dafür sorgen, dass das Array der MENUMEMBER_t
> immer mit einem Null Wert abgeschlossen ist.

Wobei (hab ich auch gerade erst gelernt) es sich dann um eine gcc 
Extension handeln würde. Im C Standard ist das nicht erlaubt, eine 
Struktur mit einem flexible array member zu initialisieren. d.h. man 
kann natürlich die Struktur schon initialisieren nur eben den flexible 
array member nicht.

von Karl H. (kbuchegg)


Lesenswert?

Ich seh das pragmatisch so:

C war von Anfang an darauf ausgelegt, dass alles zur Compile-Time eine 
fixe und feststehende Größe hat. Egal ob das Arrays oder Strukturen 
waren, alles hatte seine Größe und der Compiler konnte die feststellen. 
Hatte man keine fixen Größen, dann musste man über Pointer und alloca 
oder malloc oder ... gehen. Fixe Größe konnte auch sein, dass der 
Compiler Initialisierungen abzählt.

Was ja bis hier her auch noch kein Problem wäre.
Bei Strukturen kommt jetzt noch dazu, dass alle jemals von diesem 
Strukturtyp erzeugten Objekte auch die gleiche Größe haben müssen. Denn 
wenn dem nicht so wäre, dann würde man in Schwierigkeiten kommen, wenn 
man ein Array von diesem Strukturtyp anlegen würde.

Bis dahin war noch alles in Ordnung.

Dann allerdings gab es noch die Fälle, in denen man tatsächlich ein 
Strukturobjekt bauen möchte, welches einen variablen Anteil hat. Man 
behalf sich mit Krücken, in dem man eine struct definierte, welches als 
letzten member ein Array der Länge 1 vorsah (damit dort etwas 
Array-ähnliches steht) allokierte allerdings die Objekte immer per 
malloc, wobei man eine entsprechende Speichergröße errechnete, so dass 
die Datenmenge da rein passt, die man haben wollte. Arrays davon baute 
man einfach nicht und wenn dann waren es Pointer Arrays, deren Elemente 
dynamisch angelegt wurden.
Womit dann auch ganz automatisch der Themenkreis Initialisierung keiner 
war, denn bei dynamischer Allokierung kann man nichts (ausser bei 
calloc) initialisieren.

Ab da war dann das meiste nur noch Kosmetik um diesen 'Hack' zu 
verschönern. Denn an den grundsätzlichen Problem, dass ein Array keine 
Größeninformation mit sich rumträgt, kann man nichts ändern. Da würde 
zuviel Code den Bach runter gehen, wenn man da groß umrührt. Man denke 
nur an die Fälle, die im Zusammenhang mit Parameterpassing an Funktionen 
stehen. Zwischen Arrays und Pointer besteht nun mal in C ein enger 
Zusammenhang.
Also kann sich auch ein Strukturobjekt nicht implizit merken, wie gross 
sein flexible array member ist. Ganz abgesehen davon, dass das das 
Problem der Array Bildung von solchen Strukturen nicht lösen würde. Die 
ist ja immer noch davon abhängig, dass alle Strukturobjekte im Array die 
gleiche Größe haben.

: Bearbeitet durch User
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.