Namespaces
Variants

Struct declaration

From cppreference.net

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)
1) Struct-Definition: führt den neuen Typ struct name ein und definiert seine Bedeutung
2) Wenn in einer eigenen Zeile verwendet, wie in 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 . oder -> mit dem Namen des flexiblen Array-Mitglieds als rechtsseitigen Operanden verwendet), dann verhält sich die Struktur so, als hätte das Array-Mitglied die größte Größe, die in den für dieses Objekt allokierten Speicher passt. Wenn kein zusätzlicher Speicher allokiert wurde, verhält es sich wie ein Array mit 1 Element, außer dass das Verhalten undefiniert ist, wenn auf dieses Element zugegriffen wird oder ein Zeiger hinter dieses Element erzeugt wird. Initialisierung und der Zuweisungsoperator ignorieren das flexible Array-Mitglied. sizeof lässt es aus, kann aber mehr nachfolgendes Padding haben als das Weglassen implizieren würde. Strukturen mit flexiblen Array-Mitgliedern (oder Unions, die ein rekursives möglicherweise Struktur-Mitglied mit flexiblem Array-Mitglied haben) können nicht als Array-Elemente oder als Mitglieder anderer Strukturen auftreten.

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

struct

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