Lesson content
Read, practise, then check your understanding
Advanced C features help express low-level interfaces and reusable libraries, but each has portability and readability trade-offs.
typedef and opaque types
typedef introduces an alias, not a distinct runtime type.
typedef unsigned long UserId;
typedef int (*Comparator)(const void *, const void *);
Function-pointer aliases make callback APIs readable. Libraries can hide representation with an incomplete type:
// session.h
typedef struct Session Session;
Session *session_create(void);
void session_destroy(Session *session);
Only the implementation defines struct Session, preventing callers from depending on private fields.
Callbacks and dispatch tables
struct Handler {
const char *name;
int (*run)(const char *input, void *context);
};
void *context carries caller state, avoiding globals. Document callback lifetime, reentrancy, thread behavior, and whether the callback may retain pointers.
Bit-fields
struct Flags {
unsigned ready : 1;
unsigned mode : 3;
};
Bit-fields can compact in-memory flags, but allocation order, alignment, and interaction with hardware formats are implementation-defined. Do not assume a bit-field structure matches a network packet or device register. Explicit masks over fixed-width unsigned integers are usually more portable.
Qualifiers
const restricts modification through an access path. volatile tells the implementation accesses are observable and must not be optimized away in ordinary ways; it does not provide atomicity or thread synchronization. _Atomic and <stdatomic.h> provide language-level atomic operations when supported.
restrict is a promise that, for the relevant execution, an object accessed through one restricted pointer is not also accessed through an incompatible alias. It can enable optimization, but violating the promise is undefined behavior.
Flexible array members
struct Packet {
size_t length;
unsigned char data[];
};
struct Packet *packet = malloc(sizeof *packet + payload_length);
The flexible array must be the final member of a structure with another named member. Check addition overflow, store the allocated length, and free the complete object once.
Generic and compile-time facilities
_Generic can select an expression by type and _Static_assert verifies compile-time assumptions. Use them to strengthen small abstractions, not to recreate an unreadable alternate language in macros.
Knowledge check
Answer every question correctly to complete this chapter.
0 of 10 checks passed
Your progress is saved on this device.