Ein Array hält viele Werte eines Typs. Eine struct tut das Gegenteil: Sie hält ein paar Werte verschiedener Typen und behandelt sie als ein Ding. Ein Punkt ist ein x und ein y. Ein Angestellter ist ein Name, eine Nummer und ein Gehalt. Ohne Strukturen würdest du diese als drei lose Variablen herumtragen und hoffen, nie zu verwechseln, wessen Gehalt wessen ist.
Einen Strukturtyp deklarieren
Eine Strukturdeklaration benennt ein Tag und listet die Elemente in geschweiften Klammern:
struct Point {
int x;
int y;
};
Das Semikolon nach der schließenden Klammer ist Pflicht - es zu vergessen erzeugt eine der verwirrendsten Fehlermeldungen, die ein C-Anfänger trifft, weil der Compiler die nächste Zeile beschuldigt.
Das deklariert einen Typ, keine Variable. In C lautet der vollständige Name des Typs struct Point, mit dem Schlüsselwort:
Elemente werden mit dem Punktoperator gelesen und geschrieben, p.x. Eine Strukturvariable ist eine gewöhnliche Variable: Sie liegt auf dem Stack, sie kann mit = einer anderen Struktur desselben Typs zugewiesen werden, und sie verschwindet am Ende ihres Gültigkeitsbereichs.
Überall struct Point zu schreiben ermüdet, weshalb fast jeder echte C-Code die Deklaration in ein typedef wickelt, sodass der Typ einfach Point heißt.
Eine Struktur initialisieren
Du kannst eine Struktur in dem Moment füllen, in dem du sie deklarierst. Die Positionsform listet Werte in der Deklarationsreihenfolge:
struct Point p = {3, 4}; // x = 3, y = 4
Der benannte Initialisierer nennt stattdessen jedes Element, was klarer und sicherer ist:
Drei bemerkenswerte Dinge:
- Die benannte Form darf Elemente in beliebiger Reihenfolge aufführen, ein später hinzugefügtes oder umsortiertes Element schiebt Werte also nicht still in die falschen Plätze.
- Jedes ausgelassene Element wird mit null initialisiert -
{0}ist die idiomatische Art, eine ganze Struktur zu nullen. - Ein Element
char name[32]ist ein echtes Array innerhalb der Struktur, die Zeichenkette wird also in der Struktur selbst gespeichert, nicht anderswo. Das bedeutet auch, dassa.name = "Ada";nachträglich unerlaubt ist; du brauchststrcpy(siehe Zeichenkettenfunktionen).
Eine Struktur ohne Initialisierer ist uninitialisiert, und ihre Elemente zu lesen, bevor man sie schreibt, ist undefiniertes Verhalten. = {0} kostet nichts und beseitigt die ganze Fehlerkategorie.
Zuweisen und vergleichen
Die Strukturzuweisung kopiert jedes Element:
a ist unberührt: b hat seine eigene Kopie bekommen. Beachte aber, was C dir nicht gibt - es gibt kein == für Strukturen. if (a == b) kompiliert nicht. Vergleichen heißt Elemente vergleichen:
if (a.x == b.x && a.y == b.y) { /* gleich */ }
Greife nicht als Abkürzung zu memcmp. Compiler fügen zwischen Elementen unsichtbare Füllbytes zur Ausrichtung ein, und diese Bytes können Müll enthalten, zwei Strukturen mit identischen Elementen können also Byte für Byte ungleich sein.
Arrays von Strukturen
Weil eine Struktur ein Typ wie jeder andere ist, kannst du ein Array davon anlegen - die übliche Art, eine Tabelle von Datensätzen zu halten.
staff[i].salary liest sich als „Element i, dann dessen Element salary" - das Indizieren bindet stärker als der Punkt, es braucht also keine Klammern.
Strukturen werden in Funktionen hineinkopiert
Das ist die Regel, die die Leute erwischt. Wenn du eine Struktur an eine Funktion übergibst, kopiert C sie. Die Funktion arbeitet auf ihrer eigenen privaten Kopie:
p ist weiterhin (3, 4). Eine Struktur als Wert zu lesen, wie distance_from_origin es tut, ist vollkommen in Ordnung und bei kleinen Strukturen oft die klarste Wahl. Muss die Funktion aber die Struktur des Aufrufers ändern - oder ist die Struktur groß genug, dass das Kopieren verschwenderisch ist -, übergib stattdessen einen Zeiger. Das ist das Thema von Strukturen und Zeiger.
Eine Struktur als Wert zurückzugeben funktioniert genauso und ist völlig erlaubt:
struct Point make_point(int x, int y) {
struct Point p = {x, y};
return p;
}
Verschachtelte Strukturen
Ein Strukturelement kann selbst eine Struktur sein. Verkette die Punkte, um hineinzugreifen:
b.published.year geht Ebene für Ebene hinein. Verschachtelung ist der Weg, in C echte Datenmodelle zu bauen - und die innere Struktur wird in der äußeren gespeichert, nicht nur angezeigt, das ganze Book ist also ein zusammenhängender Speicherblock.
Größe, Füllbytes und Elementreihenfolge
sizeof sagt dir, wie groß eine Struktur ist, und die Antwort ist oft größer als die Summe ihrer Elemente:
Auf einer typischen Maschine hat Wasteful 12 Bytes und Tidy 8, obwohl beide dieselben drei Elemente halten. Der Compiler fügt Füllbytes ein, damit jedes Element an einer Adresse sitzt, die seinem Typ gefällt - ein int will meist ein Vielfaches von 4. Größere Elemente vor kleineren zu gruppieren packt tendenziell besser.
Das zählt selten (ein Array mit einer Million Elementen, das Layout eines Netzpakets) und nie für die Korrektheit deines eigenen Codes. Was es erklärt, ist, warum sizeof dich überrascht und warum memcmp auf Strukturen eine schlechte Idee ist.
Häufig gestellte Fragen
Wie deklariert man eine Struktur in C?
Schreibe struct, gefolgt von einem Tag-Namen und einer geklammerten Liste von Elementen: struct Point { int x; int y; };. Das deklariert einen Typ, keine Variable. Um eine Variable zu erzeugen, schreibst du dann struct Point p; - das Wort struct gehört in C zum Typnamen, weshalb der meiste Code es mit einem typedef paart.
Was ist der Unterschied zwischen . und -> bei Strukturen?
. und -> bei Strukturen?Nimm ., wenn du die Struktur selbst hast (p.x), und ->, wenn du einen Zeiger darauf hast (ptr->x). Der Pfeil ist die Kurzform für (*ptr).x. Siehe Strukturen und Zeiger für die Einzelheiten.
Werden Strukturen in C als Wert oder als Referenz übergeben?
Als Wert, immer. Eine Struktur an eine Funktion zu übergeben kopiert jedes Element, Änderungen innerhalb der Funktion betreffen die Variable des Aufrufers also nicht. Um das Original zu ändern - oder um das Kopieren einer großen Struktur zu vermeiden - übergib stattdessen einen Zeiger.
Wie initialisiere ich eine Struktur in C?
Entweder nach Position, struct Point p = {3, 4};, oder mit benannten Initialisierern, struct Point p = {.y = 4, .x = 3};. Die benannte Form nennt jedes Element und übersteht daher, dass jemand die Struktur später umsortiert, und lässt ungenannte Elemente genullt.