The extern keyword in C declares that a variable or function exists — but is defined somewhere else. It tells the compiler “trust me, this symbol is defined in another translation unit; the linker will resolve it.” Understanding extern is essential for any multi-file C program, and confusing declaration with definition is one of the most common C beginner mistakes.
Declaration vs Definition
The distinction is fundamental:
| Declaration | Definition | |
|---|---|---|
| What it does | Tells the compiler a symbol exists and its type | Allocates storage; creates the symbol |
| How many times | As many times as needed | Exactly once per program |
| Storage allocated | No | Yes |
| Example | extern int count; |
int count = 0; |
A variable declaration with an initialiser is always a definition, even if prefixed with extern:
extern int x; /* declaration — no storage allocated */
extern int x = 5; /* DEFINITION despite extern — initialiser forces it */
int x = 5; /* also a definition */
How extern Works in a Multi-File Program
Here is the canonical two-file example:
/* ── file1.c ── defines the variable ─────────────────────── */
#include <stdio.h>
int counter = 0; /* definition: storage allocated here */
void increment(void)
{
counter++;
}
/* ── file2.c ── uses the variable from file1.c ───────────── */
#include <stdio.h>
extern int counter; /* declaration: no storage; tells compiler type */
void increment(void); /* function declaration */
int main(void)
{
printf("counter = %d\n", counter); /* 0 */
increment();
increment();
printf("counter = %d\n", counter); /* 2 */
return 0;
}
How to Compile
gcc -ansi -Wall -Wextra -o program file1.c file2.c
./program
Sample Output
counter = 0 counter = 2
The Right Way: extern in Header Files
Repeating extern int counter; in every file that uses it is error-prone. The standard practice is to declare the variable in a header file and include it everywhere:
/* ── counter.h ─────────────────────────────────────────── */
#ifndef COUNTER_H
#define COUNTER_H
extern int counter; /* declaration — include anywhere */
void increment(void);
#endif
/* ── counter.c ─────────────────────────────────────────── */
#include "counter.h"
int counter = 0; /* definition — exactly here, exactly once */
void increment(void)
{
counter++;
}
/* ── main.c ─────────────────────────────────────────────── */
#include <stdio.h>
#include "counter.h" /* gets the extern declaration */
int main(void)
{
printf("counter = %d\n", counter);
increment();
increment();
increment();
printf("counter = %d\n", counter);
return 0;
}
gcc -ansi -Wall -Wextra -o program counter.c main.c
./program
counter = 0 counter = 3
extern for Functions
Functions have external linkage by default in C — you don’t need extern for function declarations. These two are identical:
extern void increment(void); /* explicit extern */
void increment(void); /* implicit extern — preferred */
Both are declarations (no function body). Including either in a header file works correctly. The explicit extern on a function is legal but redundant.
extern vs static — Linkage at a Glance
| Declaration | Linkage | Visible to other files? | Use case |
|---|---|---|---|
int x = 0; (file scope) |
External | Yes | Shared global variable |
extern int x; |
External (declaration only) | Yes | Use a global from another file |
static int x = 0; (file scope) |
Internal | No — file-private | Module-internal state |
static int x; (inside function) |
No linkage | No | Persistent local state |
Prefer static over global variables whenever possible. If a variable only needs to be accessed within one file, mark it static at file scope. This prevents other translation units from accidentally depending on it, reduces coupling, and enables better compiler optimisations.
Common Mistakes
- Defining the variable in the header: if you write
int counter = 0;(withoutextern) in a header that is included by multiple files, you get a “multiple definition” linker error — the storage is allocated in every translation unit that includes the header. extern int x = 5;is a definition: the initialiser forces storage allocation. Theexternqualifier is ignored for initialised declarations.- Missing the definition entirely: if you declare
extern int counter;but never defineint counter;anywhere, the linker reports “undefined reference to counter”. - Type mismatch: declaring
extern int x;but defininglong x;in another file compiles without error but causes silent data corruption at runtime. The header file pattern prevents this — both files include the same declaration.
extern in C++ Programs (extern "C")
When calling C code from C++, name mangling causes linker errors — C++ encodes argument types in function names, but C-compiled code uses plain names. The fix is:
/* In a C++ file, or a header that C++ code includes: */
#ifdef __cplusplus
extern "C" {
#endif
void increment(void); /* C function — no name mangling */
extern int counter;
#ifdef __cplusplus
}
#endif
This is purely a C++ concern — it has no effect when the header is included from a C file.
What This Teaches
- Declaration vs definition: the most important distinction in C’s linkage model — each symbol can be declared many times but defined exactly once
- Header file discipline: extern declarations belong in headers; definitions belong in exactly one
.cfile — the header pattern enforces this mechanically - Prefer static over global: restricting a symbol’s visibility to the file that owns it makes the codebase easier to reason about and refactor
Related Programs
- Bit Manipulation in C
- volatile Keyword in C – Why Embedded C Needs It
- assert() Macro in C
- C Aptitude Questions and Answers
As an Amazon Associate we earn from qualifying purchases.
Recommended Book
Linkage, storage classes, and the translation unit model are covered in sections 4.3–4.6 of The C Programming Language by Kernighan & Ritchie — the definitive reference for understanding how C programs are structured across multiple files. Also on Amazon.com.