Kernel-Style Macros & GCC Extensions
container_of, ARRAY_SIZE, type-safe min/max, offsetof, flexible array members, likely/unlikely, GCC builtins, statement expressions.
Eight pieces, and this is the chapter that made a lot of real kernel source actually readable instead of looking like magic macro soup.
ARRAY_SIZE and offsetof
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
int nums[10];
size_t n = ARRAY_SIZE(nums); /* 10 — the same trick from 1.3.A, wrapped as a macro */
#include <stddef.h>
struct point { int x, y; };
size_t off = offsetof(struct point, y); /* byte offset of y within the struct */container_of — the one that actually mattered
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
struct device {
int id;
struct list_node node; /* embedded, not a pointer */
};
/* given only a pointer to node, recover the enclosing device: */
struct device *dev = container_of(node_ptr, struct device, node);This is the pattern that unlocked a lot of kernel code for me: a callback often only gets handed a pointer to one embedded member (like a list_node), not the struct that contains it. container_of walks backward from that member's address by exactly its known offset (via offsetof) to recover a pointer to the whole containing struct. It's pointer arithmetic from 1.2.C, just aimed at struct layout instead of an array.
Type-safe min/max and statement expressions
#define max(a, b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; \
})A naive #define max(a,b) ((a) > (b) ? (a) : (b)) evaluates a and b twice each — a real problem if either has side effects. The GCC statement-expression form (({ ... })) evaluates each argument exactly once into a local typeof-inferred variable first, which is a GCC extension, not standard C, but it's exactly how the kernel's actual max()/min() avoid the double-evaluation trap.
likely/unlikely and flexible array members
likely(x)/unlikely(x)— hints wrapping__builtin_expect, telling the compiler which branch of anifis the common case so it can lay out the generated code for that path.- Flexible array member — a struct's last member declared as
type name[];with no size, letting one allocation hold both a fixed header and a variable-length trailing buffer in one contiguous block.
Notes from readers
Comments — via GitHub