Structures group members of different types, unions overlay them in one region of storage, and enumerations name integer constants (C17 6.7.2.1, 6.7.2.2). Their tags live in a separate name space.
Declaration syntax
struct-or-union-specifier:
struct-or-union [identifier] { struct-declaration-list }
struct-or-union identifier
struct-or-union:
struct | union
struct-declaration:
specifier-qualifier-list [struct-declarator-list] ;
static_assert-declaration
struct-declarator:
declarator
[declarator] : constant-expression
struct point {
int x, y;
};
struct point p = {1, 2};
struct point *q = &p;
p.x = 5; /* member access */
q->y = 7; /* member access through a pointer */
struct point r = p; /* assignment copies every member */
A structure may be assigned, passed to a function and returned from one by value. It
cannot be compared with ==, because there is no structure comparison operator.
A member cannot be a function or an incomplete type, so a structure may not contain itself,
but it may contain a pointer to itself.
A structure declaration does not create a new scope. A nested tag or enumeration constant declared inside it is visible in the enclosing scope, which is different from C++.
Layout and padding
The standard fixes part of the layout (C17 6.7.2.1):
- Members are stored at increasing addresses in the order declared.
- There is no padding before the first member, so a pointer to a structure converts to a pointer to its first member, and back.
- There may be unnamed padding between members and after the last one.
sizeofa structure includes any trailing padding, so an array of the structure has correctly aligned elements.
The amount of padding is implementation-defined and follows each platform’s ABI. Order the
members from largest to smallest to reduce it. The macro offsetof(type, member)
from <stddef.h> gives a member’s offset in bytes.
struct a { char c; double d; char e; }; /* offsets 0, 8, 16; size 24 */
struct b { double d; char c; char e; }; /* offsets 0, 8, 9; size 16 */
doubleAlignment
Each complete object type has an alignment, a power of two that its address must be a
multiple of (C17 6.2.8). _Alignof, spelled alignof in C23, gives
the alignment of a type. _Alignas, spelled alignas in C23,
strengthens it for an object or member (C17 6.7.5). The strictest fundamental alignment is
_Alignof(max_align_t). alignas cannot be applied to a bit-field,
a register object, a parameter or a typedef.
alignas(16) float vec[4]; /* 16-byte aligned array */
size_t n = alignof(struct point);
Packing, as with #pragma pack or __attribute__((packed)), is a
compiler extension, not standard C. A file format defined by bytes is better read with
explicit byte handling than by casting to a structure, because of padding and byte order.
Bit-fields
A bit-field is a member with a width in bits (C17 6.7.2.1).
struct-declarator:
declarator
[declarator] : constant-expression
| Declared type | Range |
|---|---|
unsigned int | 0 to 2width−1 |
signed int | The signed range for the width |
int | Signed or unsigned: implementation-defined |
bool | 0 or 1, with a width of at most 1 (C99) |
_BitInt(N) | Bit-precise bit-field (C23) |
| Other integer types | Implementation-defined whether allowed |
- The width is a non-negative integer constant expression, no larger than the width of the declared type.
- A width of 0 is allowed only for an unnamed bit-field, and forces the next bit-field to start a new unit of storage.
- An unnamed bit-field is padding:
unsigned : 3;. - A bit-field’s address cannot be taken, it cannot be the operand of
sizeof, and_Alignascannot be applied to it. - Implementation-defined: the order of bit-fields in a unit, the signedness of
intbit-fields, whether a bit-field may straddle a unit boundary, and whether atomic types are allowed.
struct flags {
unsigned ready : 1;
unsigned error : 1;
unsigned : 2; /* unnamed padding bits */
unsigned channel : 4;
};
Flexible array members
The last member of a structure with at least one other named member may be an array with no
size (C99, C17 6.7.2.1). The structure’s sizeof ignores the array, except for any
padding it may add. Extra memory allocated after the structure is the array’s storage.
struct buffer {
size_t len;
char data[];
};
struct buffer *b = malloc(sizeof *b + 100);
b->len = 100;
memset(b->data, 0, b->len);
- A structure with a flexible array member cannot be an array element or a member of another structure, and it cannot be initialised with data for the array.
- If no extra storage was allocated, access to the array is undefined behaviour.
- Assignment and copying ignore the array part.
- Older code used a one-element array for this, or a zero-length array as a compiler extension. The flexible array member replaces both.
Anonymous structures and unions
Since C11 an unnamed member of structure or union type, with no tag, is anonymous. Its members are treated as members of the enclosing structure, and they keep their layout (C17 6.7.2.1).
struct value {
int kind;
union { /* anonymous union */
int i;
double d;
};
};
struct value v;
v.d = 1.5; /* direct access to a member of the union */
Incomplete types and tags
A tag used before its contents are given names an incomplete type. A pointer to it
may be used, but no object can be made until the type is completed. This allows mutually
recursive structures, and opaque types whose contents are hidden in a .c
file.
struct node; /* an incomplete type */
struct list { struct node *head; };
struct node { int value; struct node *next; };
/* opaque type: users see only the pointer */
typedef struct database database;
database *db_open(const char *path);
Unions
All members of a union start at the same address, and the size is at least that of the largest member (C17 6.7.2.1). Only the member last stored holds a meaningful value. An initialiser sets the first member, or the member given by a designator.
union number {
int i;
float f;
};
union number n = { .f = 1.0f };
Reading a different member from the one last stored reinterprets the bytes of the value as the new type (C17 6.5.2.3), which is a defined way to reinterpret bytes, unlike a pointer cast. See Strict aliasing. If several structure members of a union share a common initial sequence, that part may be read through any of them.
Enumerations
enum-specifier:
enum [identifier] { enumerator-list [,] }
enum identifier
enum [identifier] : type { enumerator-list [,] } (C23)
enumerator:
enumeration-constant
enumeration-constant = constant-expression
- The first enumerator is 0 unless it is given a value. Each later one without a value is one more than the previous.
- Values may repeat, and later enumerators may use earlier ones.
- A trailing comma in the list is allowed since C99.
- The enumeration constants share the enclosing scope’s ordinary name space, so they must not clash with other names there.
Before C23, an enumeration constant has type int and its value must be
representable as int. The enumerated type is compatible with
char, a signed integer type or an unsigned integer type. The choice is
implementation-defined, but it must be able to represent all the enumerator values
(C17 6.7.2.2).
C23 lets an enumeration declare a fixed underlying type. Each constant then has the
enumerated type, and the enumeration is compatible with that underlying type. Without a
fixed type, a constant whose value does not fit int may have another integer
type.
enum colour { RED, GREEN, BLUE }; /* 0, 1, 2 */
enum flags { READ = 1, WRITE = 2, EXEC = 4 };
enum level { LOW = 10, MID, HIGH = 30 }; /* 10, 11, 30 */
enum byte : unsigned char { ZERO, MAX = 255 }; /* C23 */
An enumerated type is an integer type. Any integer can be assigned to it without a cast, and a value that matches no enumerator is allowed.