C Mastery / Linkage: internal, external, none; extern, static
Part 1 — The Core Language

Linkage: internal, external, none; extern, static

This chapter explains linkage — how identifiers with the same name in different translation units (or the same one) refer to the same or different entities. It covers extern and static in their storage-class roles.

Why This Matters

Linkage is what makes multi-file programs work. Without a precise model of internal vs. external linkage, you cannot correctly share globals across files or keep implementation details private.

Prerequisites

Core Concept

What linkage is

An identifier's linkage determines whether it refers to the same entity as an identifier of the same name in another scope or translation unit.

LinkageMeaning
ExternalVisible across the whole program; refers to the same entity in all TUs
InternalVisible only within one translation unit
NoneNo linkage (e.g., block-scope locals, labels, typedefs)

The two storage-class keywords

already-declared external identifier).

the TU); at block scope it gives static storage duration.

Syntax

/* external linkage */
extern int shared;
int shared = 0;          /* definition with external linkage */

/* internal linkage */
static int private;      /* visible only in this TU */

/* none (block scope, no linkage) */
int local;

Examples

Sharing a global across files

/* counter.h */
extern int counter;      /* declaration: the definition is elsewhere */

/* counter.c */
int counter = 0;         /* the one external definition */
/* main.c */
#include "counter.h"
#include <stdio.h>
int main(void) {
    printf("%d\n", counter);
    return 0;
}

Keeping a helper private

/* util.c */
static int helper(int x) { return x * 2; }  /* internal linkage */

int public_api(int x) { return helper(x); }  /* external linkage */

helper is not visible outside util.c.

static at block scope

void f(void)
{
    static int calls = 0;   /* static storage, but NO linkage */
    calls++;
}

The block-scope static has no linkage but static storage duration.

How It Works

The compiler records each identifier's linkage in the symbol table. External symbols are exposed to the linker; internal symbols are kept local to the object file; no-linkage identifiers are purely local to a function/block and may not even get a symbol.

Variations

Tentative definitions and linkage

At file scope, int x; without extern is a tentative definition with external linkage (unless later declared static). If no other definition appears, it becomes a definition.

static functions

A static function has internal linkage and is callable only within its TU. This is the standard way to make private helper functions.

Multiple declarations

You can declare the same external identifier many times; the declarations must be compatible. Exactly one definition may exist (or one tentative definition that becomes a definition).

Common Mistakes

definition in every includer (duplicate definition).

(linkage).

Undefined Behavior

constraint violation and, if undiagnosed, UB.

Portability

language-level guarantees are universal.

Under the Hood

External symbols appear in the object file's symbol table with global binding; internal symbols have local binding; no-linkage identifiers are typically stack-relative or register-allocated and have no symbol. The linker merges global symbols and errors on duplicates.

Practical Usage

Exercises

1. Create three files: a header with extern int x;, a .c file defining x, and a main using it. Build and run. 2. Add a static function to a file and try to call it from another file; observe the linker error. 3. Explain the difference between static int x; at file scope and inside a function.

Deep Challenge

Explain why extern int x; in a header plus int x; in exactly one .c file is correct, but int x; in the header (without extern) is a bug. Describe the exact linker diagnostic and how tentative definitions interact.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.lang.linkage06
c.lang.extern05
c.lang.static05