Struct declaration

From cppreference.com
< c‎ | language

A struct is a type consisting of a sequence of members whose storage is allocated in an ordered sequence (as opposed to union, which is a type consisting of a sequence of members whose storage overlaps).

The type specifier for a struct is identical to the union type specifier except for the keyword used:

Contents

[edit] Syntax

struct name(optional) { struct-declaration-list } (1)
struct name (2)
name - the name of the struct that's being defined
struct-declaration-list - any number of variable declarations, bit field declarations, and static assert declarations. Members of incomplete type and members of function type are not allowed (except for the flexible array member described below)

[edit] Explanation

Within a struct object, addresses of its elements (and the addresses of the bit field allocation units) increase in order in which the members were defined. A pointer to a struct can be cast to a pointer to its first member (or, if the member is a bit field, to its allocation unit). Likewise, a pointer to the first member of a struct can be cast to a pointer to the enclosing struct. There may be unnamed padding between any two members of a struct or after the last member, but not before the first member. The size of a struct is at least as large as the sum of the sizes of its members.

If a struct defines at least one named member, it is allowed to additionally declare its last member with incomplete array type. When an element of the flexible array member is accessed (in an expression that uses operator . or -> with the flexible array member's name as the right-hand-side operand), then the struct behaves as if the array member had the longest size fitting in the memory allocated for this object. If no additional storage was allocated, it behaves as if an array with 1 element, except that the behavior is undefined if that element is accessed or a pointer one past that element is produced. Initialization, sizeof, and the assignment operator ignore the flexible array member. Structures with flexible array members (or unions whose last member is a structure with flexible array member) cannot appear as array elements or as members of other structures.

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) + 46); // as if d was double d[5]
 
    s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]
    s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access
    double *dp = &(s1->d[0]);    //  OK
    *dp = 42;                    //  OK
    dp = &(s2->d[0]);            //  OK
    *dp = 42;                    //  undefined behavior
 
    *s1 = *s2; // only copies s.n, not any element of s.d
               // except those caught in sizeof (struct s)
(since C99)

Similar to union, an unnamed member of a struct whose type is a struct without name is known as anonymous struct. Every member of an anonymous struct is considered to be a member of the enclosing struct or union. This applies recursively if the enclosing struct or union is also anonymous.

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

Similar to union, the behavior of the program is undefined if struct is defined without any named members (including those obtained via anonymous nested structs or unions).

(since C11)

[edit] Notes

Because members of incomplete type are not allowed, and a stuct type is not complete until the end of the definition, a struct cannot have a member of its own type. A pointer to its own type is allowed, and is commonly used to implement nodes in linked lists or trees.

Because a struct declaration does not establish scope, nested types, enumerations and enumerators introduced by declarations within struct-declaration-list are visible in the surrounding scope where the struct is defined.

[edit] Example

#include <stddef.h>
#include <stdio.h>
 
int main(void)
{
    struct car { char *make; char *model; int year; }; // declares the struct type
    // declares and initializes an object of a previously-declared struct type
    struct car c = {.year=1923, .make="Nash", .model="48 Sports Touring Car"};
    printf("car: %d %s %s\n", c.year, c.make, c.model);
 
    // declares a struct type, an object of that type, and a pointer to it
    struct spaceship { char *make; char *model; char *year; }
        ship = {"Incom Corporation", "T-65 X-wing starfighter", "128 ABY"},
        *pship = &ship;
    printf("spaceship: %s %s %s\n", ship.year, ship.make, ship.model);
 
    // addresses increase in order of definition
    // padding may be inserted
    struct A { char a; double b; char c;};
    printf("offset of char a = %zu\noffset of double b = %zu\noffset of char c = %zu\n"
           "sizeof(struct A)=%zu\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("offset of char a = %zu\noffset of char b = %zu\noffset of double c = %zu\n"
           "sizeof(struct B)=%zu\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* pmake = (char*)&ship;
    pship = (struct spaceship *)pmake;
}

Possible output:

car: 1923 Nash 48 Sports Touring Car
spaceship: 128 ABY Incom Corporation T-65 X-wing starfighter
offset of char a = 0
offset of double b = 8
offset of char c = 16
sizeof(struct A)=24
offset of char a = 0
offset of char b = 1
offset of double c = 8
sizeof(struct B)=16

[edit] See also