Struct declaration
Ein Struct ist ein Typ, der aus einer Sequenz von Mitgliedern besteht, deren Speicher in einer geordneten Sequenz alloziert wird (im Gegensatz zu einer Union, die ein Typ ist, der aus einer Sequenz von Mitgliedern besteht, deren Speicher sich überlappt).
Der
Typbezeichner
für eine Struktur ist identisch mit dem
union
Typbezeichner, abgesehen vom verwendeten Schlüsselwort:
Inhaltsverzeichnis |
Syntax
struct
attr-spec-seq
(optional)
name
(optional)
{
struct-declaration-list
}
|
(1) | ||||||||
struct
attr-spec-seq
(optional)
name
|
(2) | ||||||||
struct
name
;
,
deklariert
es die Struktur
name
, definiert sie aber nicht (siehe Vorwärtsdeklaration unten). In anderen Kontexten benennt es die zuvor deklarierte Struktur, und
attr-spec-seq
ist nicht erlaubt.
| name | - | der Name der Struktur, die definiert wird |
| struct-declaration-list | - | beliebige Anzahl von Variablendeklarationen, Bitfeld -Deklarationen und static_assert -Deklarationen. Elemente unvollständigen Typs und Elemente vom Funktionstyp sind nicht zulässig (außer dem flexiblen Array-Element, das unten beschrieben wird) |
| attr-spec-seq | - | (C23) optionale Liste von Attributen , angewendet auf den Strukturtyp |
Erklärung
Innerhalb eines Struct-Objekts erhöhen sich die Adressen seiner Elemente (und die Adressen der Bitfeld-Belegungseinheiten) in der Reihenfolge, in der die Member definiert wurden. Ein Zeiger auf einen Struct kann in einen Zeiger auf sein erstes Member umgewandelt werden (oder, wenn das Member ein Bitfeld ist, in seine Belegungseinheit). Ebenso kann ein Zeiger auf das erste Member eines Structs in einen Zeiger auf den umschließenden Struct umgewandelt werden. Es kann unbenanntes Padding zwischen zwei beliebigen Members eines Structs oder nach dem letzten Member geben, jedoch nicht vor dem ersten Member. Die Größe eines Structs ist mindestens so groß wie die Summe der Größen seiner Member.
|
Wenn eine Struktur mindestens ein benanntes Mitglied definiert, ist es zulässig, zusätzlich ihr letztes Mitglied mit unvollständigem Array-Typ zu deklarieren. Wenn auf ein Element des flexiblen Array-Mitglieds zugegriffen wird (in einem Ausdruck, der den Operator
struct s { int n; double d[]; }; // s.d ist ein flexibles Array-Mitglied struct s t1 = { 0 }; // OK, d ist wie double d[1], aber UB beim Zugriff struct s t2 = { 1, { 4.2 } }; // Fehler: Initialisierung ignoriert flexibles Array // wenn sizeof (double) == 8 struct s *s1 = malloc(sizeof (struct s) + 64); // als ob d double d[8] wäre struct s *s2 = malloc(sizeof (struct s) + 40); // als ob d double d[5] wäre s1 = malloc(sizeof (struct s) + 10); // jetzt als ob d double d[1] wäre. Zwei Bytes überschüssig. double *dp = &(s1->d[0]); // OK *dp = 42; // OK s1->d[1]++; // Undefiniertes Verhalten. 2 überschüssige Bytes können nicht // als double zugegriffen werden. s2 = malloc(sizeof (struct s) + 6); // gleich, aber UB beim Zugriff, weil 2 Bytes // fehlen, um 1 double zu vervollständigen dp = &(s2->d[0]); // OK, Adresse kann problemlos genommen werden *dp = 42; // undefiniertes Verhalten *s1 = *s2; // kopiert nur s.n, kein Element von s.d // außer denen, die in sizeof (struct s) erfasst werden |
(seit C99) |
|
Ähnlich wie bei union wird ein unbenanntes Mitglied einer struct, dessen Typ eine struct ohne name ist, als anonyme struct bezeichnet. Jedes Mitglied einer anonymen struct wird als Mitglied der umschließenden struct oder union betrachtet, wobei deren Strukturlayout erhalten bleibt. Dies gilt rekursiv, wenn die umschließende struct oder union ebenfalls anonym ist. struct v { union // anonymous union { struct { int i, j; }; // anonymous structure struct { long k, l; } w; }; int m; } v1; v1.i = 2; // valid v1.k = 3; // invalid: inner structure is not anonymous v1.w.k = 5; // valid Ähnlich wie bei union ist das Verhalten des Programms undefiniert, wenn struct ohne benannte Mitglieder definiert wird (einschließlich derer, die durch anonyme verschachtelte structs oder unions erhalten wurden). |
(since C11) |
Forward-Deklaration
Eine Deklaration der folgenden Form
struct
attr-spec-seq
(optional)
name
;
|
|||||||||
verbirgt jede zuvor deklarierte Bedeutung für den Namen name im Tag-Namensraum und deklariert name als einen neuen Struct-Namen im aktuellen Gültigkeitsbereich, der später definiert wird. Bis die Definition erscheint, hat dieser Struct-Name einen unvollständigen Typ .
Dies erlaubt Strukturen, die sich gegenseitig referenzieren:
struct y; struct x { struct y *p; /* ... */ }; struct y { struct x *q; /* ... */ };
Beachten Sie, dass ein neuer Struct-Name auch allein durch die Verwendung eines Struct-Tags innerhalb einer anderen Deklaration eingeführt werden kann, aber wenn ein zuvor deklarierter Struct mit demselben Namen im Tag- Namensraum existiert, würde der Tag auf diesen Namen verweisen.
struct s* p = NULL; // Tag, der eine unbekannte Struktur benennt, deklariert sie struct s { int a; }; // Definition für die Struktur, auf die p zeigt void g(void) { struct s; // Vorwärtsdeklaration einer neuen, lokalen Struktur s // verdeckt die globale Struktur s bis zum Ende dieses Blocks struct s *p; // Zeiger auf lokale Struktur s // ohne die obige Vorwärtsdeklaration // würde dies auf die Dateibereichs-s zeigen struct s { char* p; }; // Definition der lokalen Struktur s }
Schlüsselwörter
Hinweise
Siehe struct initialization für die Regeln bezüglich der Initialisierer für Structs.
Da Elemente eines unvollständigen Typs nicht erlaubt sind und ein Struct-Typ erst am Ende der Definition vollständig ist, kann ein Struct kein Element seines eigenen Typs enthalten. Ein Zeiger auf seinen eigenen Typ ist jedoch erlaubt und wird häufig zur Implementierung von Knoten in verknüpften Listen oder Bäumen verwendet.
Da eine Struct-Deklaration keinen Scope etabliert, sind geschachtelte Typen, Enumerationen und Enumeratoren, die durch Deklarationen innerhalb der struct-declaration-list eingeführt werden, im umgebenden Scope sichtbar, in dem das Struct definiert ist.
Beispiel
#include <stddef.h> #include <stdio.h> int main(void) { // Deklariere den Strukturtyp. struct car { char* make; int year; }; // Deklariere und initialisiere ein Objekt eines zuvor deklarierten Strukturtyps. struct car c = {.year = 1923, .make = "Nash"}; printf("1) Car: %d %s\n", c.year, c.make); // Deklariere einen Strukturtyp, ein Objekt dieses Typs und einen Zeiger darauf. struct spaceship { char* model; int max_speed; } ship = {"T-65 X-wing starfighter", 1050}, *pship = &ship; printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed); // Adresszunahme in der Reihenfolge der Definition. Padding kann eingefügt werden. struct A { char a; double b; char c; }; printf( "3) Offset of char a = %zu\n" "4) Offset of double b = %zu\n" "5) Offset of char c = %zu\n" "6) Size of struct A = %zu\n\n", offsetof(struct A, a), offsetof(struct A, b), offsetof(struct A, c), sizeof(struct A) ); struct B { char a; char b; double c; }; printf( "7) Offset of char a = %zu\n" "8) Offset of char b = %zu\n" "9) Offset of double c = %zu\n" "A) Size of struct B = %zu\n\n", offsetof(struct B, a), offsetof(struct B, b), offsetof(struct B, c), sizeof(struct B) ); // Ein Zeiger auf eine Struktur kann zu einem Zeiger // auf ihr erstes Element gecastet werden und umgekehrt. char** pmodel = (char **)pship; printf("B) %s\n", *pmodel); pship = (struct spaceship *)pmodel; }
Mögliche Ausgabe:
1) Car: 1923 Nash 2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h 3) Offset of char a = 0 4) Offset of double b = 8 5) Offset of char c = 16 6) Size of struct A = 24 7) Offset of char a = 0 8) Offset of char b = 1 9) Offset of double c = 8 A) Size of struct B = 16 B) T-65 X-wing starfighter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| DR 499 | C11 | Elemente von anonymen Structs/Unions wurden als Elemente des umschließenden Structs/Union betrachtet | sie behalten ihr Speicherlayout bei |
Referenzen
- C23-Standard (ISO/IEC 9899:2024):
-
- 6.7.2.1 Struktur- und Unionspezifizierer (S.: TBD)
- C17-Standard (ISO/IEC 9899:2018):
-
- 6.7.2.1 Struktur- und Unionspezifizierer (S: 81-84)
- C11-Standard (ISO/IEC 9899:2011):
-
- 6.7.2.1 Struktur- und Unionspezifizierer (S: 112-117)
- C99-Standard (ISO/IEC 9899:1999):
-
- 6.7.2.1 Struktur- und Unionspezifizierer (S: 101-104)
- C89/C90 Standard (ISO/IEC 9899:1990):
-
- 3.5.2.1 Struktur- und Unionspezifizierer
Siehe auch
|
C++ Dokumentation
für
Klassendeklaration
|