C Mastery / Inline Functions and Linkage
Part 1 — The Core Language

Inline Functions and Linkage

This chapter covers the inline keyword (C99) and how it interacts with linkage. Inline functions are a performance hint, but their rules across translation units are notoriously subtle.

Why This Matters

inline lets you put function definitions in headers without causing duplicate definition errors, and gives the compiler a hint to inline the call. Getting the linkage rules wrong produces exactly those duplicate-definition errors or "undefined reference" surprises.

Prerequisites

Core Concept

The inline keyword

inline is a function specifier (C99 and later) that suggests the compiler inline the function's body at call sites. It is a *hint*, not a command; the compiler may ignore it.

inline int max(int a, int b)
{
    return a > b ? a : b;
}

The linkage problem

An inline function defined in a header included by many translation units must not produce a duplicate external definition at link time. C has specific rules for this:

definition in the entire program, and that definition must be in a translation unit where the function is declared extern (not inline) or defined without inline.

each translation unit; each TU gets its own copy, and there is no external definition to collide.

The common, portable idiom is:

/* header.h */
static inline int max(int a, int b) { return a > b ? a : b; }

static inline is the simplest and most portable approach: each TU gets its own copy, with no linker involvement.

Syntax

inline return_type name(params) { body }        /* external-linkage inline */
static inline return_type name(params) { body } /* internal-linkage inline */

Examples

static inline in a header

/* util.h */
#ifndef UTIL_H
#define UTIL_H

static inline int square(int x)
{
    return x * x;
}

#endif
/* main.c */
#include "util.h"
int main(void) { return square(4); }

Each TU that includes util.h gets its own square; there is no duplicate definition because it is static.

External inline (C99 semantics)

/* header.h */
inline int max(int a, int b) { return a > b ? a : b; }

/* max.c — provides the external definition */
extern inline int max(int a, int b);

This is the C99 model: max.c provides the one external definition. It is more subtle than static inline, so most code uses static inline.

How It Works

static inline gives each TU an internal copy, which the compiler may inline or emit as a local function. External inline relies on the linker to resolve the single external definition; the exact semantics differ between C99 and C11/C17 and between compilers (notably GCC's handling of extern inline).

Variations

C99 vs. C11 inline semantics

The rules for external inline changed subtly between C99 and C11/C17, and GCC historically followed a different model (GNU89). This is a classic source of portability bugs. The static inline idiom sidesteps all of it.

__attribute__((always_inline)) and __forceinline

GCC/Clang offer __attribute__((always_inline)), and MSVC offers __forceinline. These are compiler extensions, not ISO C.

Common Mistakes

definition or missing definition errors.

Undefined Behavior

same function in the program (different bodies) is a constraint violation/UB.

Portability

reasonably in modern versions, but check your version).

Under the Hood

Inlining substitutes the function body into the caller, eliminating call overhead and enabling further optimization. static inline functions may be inlined or emitted as local symbols; external inline involves the linker.

Practical Usage

ABI/linkage.

Exercises

1. Write a static inline helper in a header and use it in two translation units; build successfully. 2. Attempt a non-static inline definition in a header and observe the linker behavior (then fix it). 3. Compile a simple inline function at -O0 and -O2 and inspect the generated assembly to see if it was inlined.

Deep Challenge

Explain the difference between these three header definitions and what linker symbols each produces:

static inline int f(int x) { return x; }

inline int g(int x) { return x; }

static int h(int x) { return x; }

Then describe a scenario where each is the right choice.

References

Verification

STANDARD-VERSION-DEPENDENT

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.func.inline05