Namensräume
Varianten
Aktionen

Struct-Deklaration

Von cppreference.com
< c‎ | Sprache

Eine Struktur ist ein Typ, der aus einer Sequenz von Membern besteht, deren Speicherplatz in einer geordneten Sequenz zugewiesen wird (im Gegensatz zu einer Union, die ein Typ ist, der aus einer Sequenz von Membern besteht, deren Speicherplatz sich überlappt).

Der Typ-Spezifizierer für eine Struktur ist identisch mit dem union Typ-Spezifizierer, außer dem verwendeten Schlüsselwort

Inhalt

[bearbeiten] Syntax

struct attr-spec-seq (optional) name (optional) { struct-declaration-list } (1)
struct attr-spec-seq (optional) name (2)
1) Strukturdefinition: führt den neuen Typ struct name ein und definiert dessen Bedeutung
2) Wenn sie allein in einer Zeile verwendet wird, wie in struct name ;, *deklariert* sie die Struktur `name`, definiert sie aber nicht (siehe unten Vorwärtsdeklaration). In anderen Kontexten benennt sie die zuvor deklarierte Struktur, und attr-spec-seq ist nicht erlaubt.
name - der Name der zu definierenden Struktur
struct-declaration-list - beliebige Anzahl von Variablendeklarationen, Bitfeld-Deklarationen und static assert-Deklarationen. Member vom unvollständigen Typ und Member vom Funktionstyp sind nicht erlaubt (außer dem flexiblen Array-Member, der unten beschrieben wird)
attr-spec-seq - (C23)optionale Liste von Attributen, die auf den Strukturtyp angewendet werden

[bearbeiten] Erklärung

Innerhalb eines Struktur-Objekts nehmen die Adressen seiner Elemente (und die Adressen der Bitfeld-Zuweisungseinheiten) in der Reihenfolge zu, in der die Member definiert wurden. Ein Zeiger auf eine Struktur kann in einen Zeiger auf ihr erstes Element (oder, wenn das Element ein Bitfeld ist, in dessen Zuweisungseinheit) umgewandelt werden. Ebenso kann ein Zeiger auf das erste Element einer Struktur in einen Zeiger auf die umgebende Struktur umgewandelt werden. Es kann unbezeichnete Auffüllung zwischen zwei beliebigen Membern einer Struktur oder nach dem letzten Member geben, aber nicht vor dem ersten Member. Die Größe einer Struktur ist mindestens so groß wie die Summe der Größen ihrer Member.

Wenn eine Struktur mindestens einen benannten Member definiert, ist es erlaubt, ihren letzten Member zusätzlich mit einem unvollständigen Array-Typ zu deklarieren. Wenn auf ein Element des flexiblen Array-Members zugegriffen wird (in einem Ausdruck, der den Operator . oder -> mit dem Namen des flexiblen Array-Members als rechtem Operand verwendet), verhält sich die Struktur so, als hätte der Array-Member die längste Größe, die in den für dieses Objekt zugewiesenen Speicher passt. Wenn kein zusätzlicher Speicher zugewiesen wurde, verhält sie sich so, als hätte sie ein Array mit 1 Element, außer dass das Verhalten undefiniert ist, wenn auf dieses Element zugegriffen wird oder ein Zeiger auf das Ende dieses Elements erzeugt wird. Initialisierung und der Zuweisungsoperator ignorieren den flexiblen Array-Member. sizeof ignoriert ihn, kann aber mehr nachfolgende Auffüllung aufweisen, als die Auslassung implizieren würde. Strukturen mit flexiblen Array-Membern (oder Unions, die einen rekursiven, möglicherweise strukturierten Member mit flexiblem Array-Member haben) dürfen nicht als Array-Elemente oder als Member anderer Strukturen erscheinen.

struct s { int n; double d[]; }; // s.d is a flexible array member
 
struct s t1 = { 0 };          // OK, d is as if double d[1], but UB to access
struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array
 
// if sizeof (double) == 8
struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8]
struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5]
 
s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess.
double *dp = &(s1->d[0]);    // OK
*dp = 42;                    // OK
s1->d[1]++;                  // Undefined behavior. 2 excess bytes can't be accessed
                             // as double.
 
s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access because 2 bytes are
                                     // missing to complete 1 double
dp = &(s2->d[0]);            // OK, can take address just fine
*dp = 42;                    // undefined behavior
 
*s1 = *s2; // only copies s.n, not any element of s.d
           // except those caught in sizeof (struct s)
(seit C99)

Ähnlich wie bei Unions wird ein unbenannter Member einer Struktur, dessen Typ eine Struktur ohne name ist, als *anonyme Struktur* bezeichnet. Jeder Member einer anonymen Struktur wird als Member der umschließenden Struktur oder Union betrachtet und behält dessen Speicherlayout. Dies gilt rekursiv, wenn die umschließende Struktur 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 Unions ist das Verhalten des Programms undefiniert, wenn eine Struktur ohne benannte Member definiert wird (einschließlich derjenigen, die über anonyme verschachtelte Strukturen oder Unions gewonnen werden).

(seit C11)

[bearbeiten] Vorwärtsdeklaration

Eine Deklaration der folgenden Form

struct attr-spec-seq (optional) name ;

verdeckt jede zuvor deklarierte Bedeutung für den Namen name im Tag-Namensraum und deklariert name als neuen Struktur-Namen im aktuellen Geltungsbereich, der später definiert wird. Bis die Definition erscheint, hat dieser Struktur-Name einen unvollständigen Typ.

Dies ermöglicht es Strukturen, die sich gegenseitig referenzieren

struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };

Beachten Sie, dass ein neuer Struktur-Name auch nur durch die Verwendung eines Struktur-Tags innerhalb einer anderen Deklaration eingeführt werden kann. Wenn jedoch eine zuvor deklarierte Struktur mit demselben Namen im Tag-Namensraum vorhanden ist, bezieht sich das Tag auf diesen Namen.

struct s* p = NULL; // tag naming an unknown struct declares it
struct s { int a; }; // definition for the struct pointed to by p
void g(void)
{
    struct s; // forward declaration of a new, local struct s
              // this hides global struct s until the end of this block
    struct s *p;  // pointer to local struct s
                  // without the forward declaration above,
                  // this would point at the file-scope s
    struct s { char* p; }; // definitions of the local struct s
}

[bearbeiten] Schlüsselwörter

struct

[bearbeiten] Hinweise

Siehe struct-Initialisierung für die Regeln bezüglich der Initialisierer für Strukturen.

Da Member vom unvollständigen Typ nicht erlaubt sind und ein Struktur-Typ erst am Ende der Definition als vollständig gilt, kann eine Struktur keinen Member ihres eigenen Typs enthalten. Ein Zeiger auf ihren eigenen Typ ist erlaubt und wird üblicherweise zur Implementierung von Knoten in verknüpften Listen oder Bäumen verwendet.

Da eine Struktur-Deklaration keinen Geltungsbereich festlegt, sind verschachtelte Typen, Aufzählungen und Aufzähler, die durch Deklarationen innerhalb von struct-declaration-list eingeführt werden, im umgebenden Geltungsbereich sichtbar, in dem die Struktur definiert wird.

[bearbeiten] Beispiel

#include <stddef.h>
#include <stdio.h>
 
int main(void)
{
    // Declare the struct type.
    struct car
    {
        char* make;
        int year;
    };
    // Declare and initialize an object of a previously-declared struct type.
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
 
    // Declare a struct type, an object of that type, and a pointer to it.
    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);
 
    // Address increase in order of definition. Padding may be inserted.
    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)
    );
 
    // A pointer to a struct can be cast to a pointer
    // to its first member and vice versa.
    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

[bearbeiten] Fehlerberichte

Die folgenden verhaltensändernden Defect Reports wurden rückwirkend auf zuvor veröffentlichte C-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
DR 499 C11 Member von anonymen Strukturen/Unions wurden als Member der umschließenden Struktur/Union betrachtet sie behalten ihr Speicherlayout

[bearbeiten] Referenzen

  • C23-Standard (ISO/IEC 9899:2024)
  • 6.7.2.1 Struktur- und Union-Spezifizierer (S. TBD)
  • C17-Standard (ISO/IEC 9899:2018)
  • 6.7.2.1 Struktur- und Union-Spezifizierer (S. 81-84)
  • C11-Standard (ISO/IEC 9899:2011)
  • 6.7.2.1 Struktur- und Union-Spezifizierer (S. 112-117)
  • C99-Standard (ISO/IEC 9899:1999)
  • 6.7.2.1 Struktur- und Union-Spezifizierer (S. 101-104)
  • C89/C90-Standard (ISO/IEC 9899:1990)
  • 3.5.2.1 Struktur- und Unionsspezifizierer

[bearbeiten] Siehe auch

C++ Dokumentation für Klassendeklaration