Mastering GNU C Extensions: typeof, Zero‑Length Arrays, and More in the Linux Kernel

This article explains key GNU C extensions used in the Linux kernel—including typeof, zero‑length (flexible) arrays, case ranges, labeled initializers, variadic macros, function and variable attributes, built‑in functions, asmlinkage, and UL suffixes—showing why they exist, how to use them safely, and providing concrete code examples.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering GNU C Extensions: typeof, Zero‑Length Arrays, and More in the Linux Kernel

typeof

The Linux kernel is compiled with GCC, which supports GNU C extensions such as typeof. A classic macro for maximum value fails when arguments have side effects (e.g., max(x++, y++)). The safe version uses temporary variables and typeof to preserve types:

#define max(a,b) ({
    typeof(a) _a = (a);
    typeof(b) _b = (b);
    (void)(&_a == &_b); // warn if types differ
    _a > _b ? _a : _b;
})

Example usage demonstrates correct evaluation without unintended increments.

Zero‑Length (Flexible) Arrays

Also called flexible or variable‑length arrays, a zero‑length array placed as the last member of a struct allows the struct to be allocated with extra space for variable data.

struct pcpu_chunk {
    struct list_head list;
    unsigned long populated[]; /* flexible array */
};

struct line {
    int length;
    char contents[0];
};

struct line *thisline = malloc(sizeof(struct line) + this_length);
thisline->length = this_length;

The size of struct line is only sizeof(int); the actual storage for contents is provided by the dynamic allocation.

Case Ranges

GNU C allows a range of values as a single case label, e.g., case low ... high:. This is useful for handling character ranges or numeric intervals in switch statements.

case '0' ... '9':
    val = 10 * val + (*name - '0');
    break;

When using numeric ranges, ensure spaces around the ellipsis ( ...) to avoid compilation errors.

Labeled Initializers

GNU C permits initializing struct members by name, regardless of order. This is common in kernel driver code, such as initializing a file_operations structure:

static const struct file_operations zero_fops = {
    .llseek   = zero_lseek,
    .read     = new_sync_read,
    .write    = write_zero,
    .read_iter= read_iter_zero,
    .aio_write= aio_write_zero,
    .mmap     = mmap_zero,
};

Uninitialized members default to zero or NULL, preserving compatibility when the structure definition changes.

Variadic Macros

GNU C supports macros with a variable number of arguments, useful for logging functions. The ##__VA_ARGS__ token removes the preceding comma when no extra arguments are supplied.

#define pr_debug(fmt, ...) \
    dynamic_pr_debug(fmt, ##__VA_ARGS__)

Function, Variable, and Type Attributes

Attributes let the compiler apply optimizations or checks. Common function attributes include format (printf‑style checking), noreturn, and const. Variable attributes such as __aligned(x) control alignment, while __packed reduces padding.

#define __pure           __attribute__((pure))
#define __aligned(x)     __attribute__((aligned(x)))
#define __printf(a,b)    __attribute__((format(printf,a,b)))
#define __scanf(a,b)     __attribute__((format(scanf,a,b)))
#define noinline         __attribute__((noinline))

Example: a function declared with __attribute__((format(printf,2,3))) will have its format string and arguments type‑checked by the compiler.

Built‑in Functions

GCC provides built‑ins like __builtin_constant_p(x) (detect compile‑time constants) and __builtin_expect(exp,c) (branch prediction hints). The kernel wraps these in macros such as LIKELY(x) and UNLIKELY(x):

#define LIKELY(x)   __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)

Another built‑in, __builtin_prefetch(addr, rw, locality), preloads data into cache to reduce latency. The kernel defines convenient wrappers:

#define prefetch(x)   __builtin_prefetch(x)
#define prefetchw(x)  __builtin_prefetch(x,1)

Example usage in mm/page_alloc.c shows prefetching page structures before processing them.

asmlinkage

On x86, asmlinkage forces a function to receive all arguments on the stack ( regparm(0)), bypassing the usual register passing convention. ARM uses the ATPCS calling convention, passing arguments in registers (R0‑R4) and does not define asmlinkage.

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

UL Suffix

Appending UL to a numeric literal forces it to be an unsigned long, preventing overflow when the expression involves large values or mixed signedness.

1 – signed integer literal

1UL – unsigned long literal

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Linux kernelmacrotypeofGNU Czero-length arraybuiltin functionfunction attribute
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.