Anzahl Elemente in Struktur
-
Wenn ich mit
struct SubItem { String a; String b; }; struct Item { String c; struct SubItemsubItem[]; };
eine Struktur anlege und mit
struct Item item[] = {{"A", {{"B1", "C1"},{"B2", "C2"}}},{"A", {{"B1", "C1"},{"B2", "C2"}}}} ;
anlege, wie bekomme ich zur Laufzeit heraus wie viele Elemente meine Struktur (und die Sub-Struktur) haben?
-
so gar nicht.
Dein "SubItemsubItem" ist im Endeffekt nichts weiter als ein Zeiger
und der weiß nichts über das, worauf er zeigt (außer vllt dessen Typ).
Dir bleibt also nichts anderes übrig als in der Item-Struktur auch noch
die Anzahl der Sub-Items zu speichern.
-
Vielen Dank, dachte mir schon sowas. Das gleiche Gilt dann natürlich auch für die oberste Ebene?
-
Du kannst dir besondere Werte überlegen, die das Ende deines Feldes Markieren. So wie '\0' bei C-Strings oder
NULL
bei Zeigern.Hier hängt es davon ab, wie dein String definiert ist:
beichar*
kannst duNULL
nehmen.
beichar[WERT]
kannst du den Leerstring "" nehmen.
-
Dabei musst du allerdings zwischen Performace und Speichereffizienz unterscheiden.
Beispiel:Im C-String wird das Ende durch ein 0-Byte dargestellt.
Um die Länge zu ermitteln muss man also durch den kompletten string iterieren und schauen, ob das aktuelle Byte den Wert 0 ('\0') hat.
Dafür verbraucht das 0-Byte aber eben nur 1 Byte Speicher.Wenn man hingegen die Länge mitspeichert (gewöhnlich in einem size_t), so kann man diese direkt abfragen. Dafür verbraucht ein size_t aber mehr Speicher.
Du könntest zwar für die Länge auch nur ein Byte verwenden, würdest dich dann aber auf maximal 255 Zeichen begrenzenEs gibt aber noch ein Problem mit der Ende-Markierung (z.B. 0-Byte):
Sollte im gesamten Puffer keine solche Ende-Markierung auftreten, so wird ein Versuch, die Länge zu ermitteln, zwangsläufig in einem Buffer-Overrun münden.
Das umgeht man, wenn man die Länge extra mitspeichert (wobei man auch hier darauf achten sollte, dass die gespeicherte Länge nicht größer sein darf, als der tatsächliche Puffer).
-
Halt, halt, halt, Moment! SubItemsubItem ist an der Stelle kein Zeiger!
Es handelt sich um einen flexible array member (siehe ISO/IEC 9899:1999 6.7.2.1 (16)). Als letzter Member eines Structs mit mehr als einem benannten Member darf ein Array unspezifizierter Länge benutzt werden, wodurch die Größe des Structs selbst (!) letztendlich variabel wird. Der Inhalt des Arrays liegt dann direkt hinter dem Rest des Structs (der eine feste Größe hat).
Hintergrund ist ein alter Trick, den man beispielsweise in POSIX bei den IPC-Funktionen msgsnd bzw. msgrcv findet. Dort sieht ein message buffer so aus:
struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ };
Und man fordert beispielsweise mit
struct msgbuf *message = malloc(offsetof(struct msgbuf, mtext) + 1024);
genug Speicher für eine Nachricht von einem Kilobyte Länge an. Da man weiß, dass hinter dem eigentlichen struct *message noch Platz ist, schreibt man einfach dort hinein, und der Code verhält sich so, als sei mtext entsprechend länger als das eine Byte, das im Struct deklariert ist.
Dieses Vorgehen wurde mit C99 quasi geadelt und standardisiert. In C99 ist es nicht mehr notwendig, eine falsche Länge anzugeben; man lässt sie einfach weg. sizeof(struct Item) ist die Größe des Reststructs (ohne das Array, dessen Länge man nicht kennt), und der Bereich hinter diesem Structkopf wird als Array behandelt. Wäre POSIX neuer als C99, so sähe dieses API wohl aus wie folgt:
struct msgbuf { long mtype; char mtext[]; }; ... struct msgbuf *message = malloc(sizeof(struct msgbuf) + 1024);
Da die Länge des Structs dadurch variabel wird, ist es nicht mehr möglich, Arrays dieses Structs zu haben -- Zeigerarithmetik funktioniert damit stumpf nicht mehr. Dein Anliegen
struct Item item[];
zu haben, ist in dieser Form nicht möglich.
-
Gut zu wissen. Das ändert aber nichts daran, dass man die Länge explizit irgendwo ablegen muss, ob nun als konkreten Wert oder mit Hilfe einer Ende-Markierung.