← Preprocessor and CompilationC PROGRAMMING 1.6.F

Kernel-Style Macros & GCC Extensions

container_of, ARRAY_SIZE, type-safe min/max, offsetof, flexible array members, likely/unlikely, GCC builtins, statement expressions.

StatusCollected
NotesChapter test: 4/5 (80%)

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 an if is 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