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
c.core.17— functions.
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:
- An
inlinefunction with external linkage must have exactly one external
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.
- An
inlinefunction with internal linkage (static inline) is local to
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
- Defining a non-static
inlinefunction in a header and getting duplicate
definition or missing definition errors.
- Confusing
inlinewithstatic inline. - Assuming
inlineguarantees inlining.
Undefined Behavior
- An
inlinefunction definition that conflicts with another definition of the
same function in the program (different bodies) is a constraint violation/UB.
Portability
static inlineis the most portable form and is available in C99+.- The external-inline model is standard but has compiler-specific nuances.
- MSVC historically had different
inlinesemantics (it supports C99inline
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
- Put small, hot functions in headers as
static inline. - Use
static inlinefor accessor functions and small math helpers. - Avoid external
inlineunless you have a specific reason and understand the
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.
Related Concepts
c.lang.linkage— linkage rules.c.core.39— multi-file programs and headers.c.opt.inlining— how inlining works.
References
- ISO/IEC 9899:2018 §6.7.4 (function specifiers).
- GCC documentation on inline functions.
Verification
inlineis a hint, not a guarantee.VERIFIEDstatic inlineis the portable idiom.VERIFIED- External inline semantics differ by standard version/compiler.
STANDARD-VERSION-DEPENDENT
- No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] inline function specifier
- [ ] static inline idiom
- [ ] External inline linkage
- [ ] C99 vs C11 inline differences
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.func.inline | 0 | 5 |