Nearby lessons

112 of 124

C - extern Storage Class

extern declares that a variable or function exists somewhere else — usually in another .c file. Learn the declaration-versus-definition rule that trips everyone up, the header-file pattern, and the linker errors that follow when you get it wrong.

The Core Idea

In simple words: the compiler works on one .c file at a time and knows nothing about the others. extern is your promise that a name will be found later by the linker. It creates no memory — it only lets the current file refer to memory that some other file defines.
Example01
CCode Cell
1/* ---------- data.c : the DEFINITION lives here ---------- */
2int sharedCount = 42; /* memory is allocated here */
3float sharedRate = 3.5f;
4 
5/* ---------- main.c : DECLARATIONS only ---------- */
6#include <stdio.h>
7 
8extern int sharedCount; /* "exists somewhere - find it" */
9extern float sharedRate;
10 
11int main()
12{
13 printf("count = %d\n", sharedCount);
14 printf("rate = %.1f\n", sharedRate);
15 
16 sharedCount = 100; /* modifies the object in data.c */
17 printf("count = %d\n", sharedCount);
18 return 0;
19}
20 
21/* Compile both together: gcc main.c data.c -o program */
Output
count = 42
rate  = 3.5
count = 100

Declaration vs Definition

This distinction is the whole topic. A definition allocates memory; a declaration only names something:

CodeKindAllocates memory?How many allowed
int x; at file scopeDefinitionYesOne per program
int x = 5;DefinitionYesOne per program
extern int x;DeclarationNoAny number
extern int x = 5;DefinitionYesOne — the initialiser wins
Example02
CCode Cell
1#include <stdio.h>
2 
3extern int value; /* declaration - no memory yet */
4extern int value; /* declaring it again is harmless */
5extern int value;
6 
7int value = 99; /* THE definition - exactly one of these */
8 
9int main()
10{
11 printf("value = %d\n", value);
12 return 0;
13}
Output
value = 99

An Initialiser Turns extern Into a Definition

extern int x = 5; is a definition, not a declaration. The initialiser overrides the extern, so if you write that line in a header included by three files, you get three definitions and a "multiple definition" link error. Headers declare; one .c file defines.
Example03
CCode Cell
1/* ---------- WRONG: config.h ----------
2extern int maxUsers = 100; <- definition in a header!
3 
4 Included by main.c and helper.c, so the linker sees:
5 multiple definition of 'maxUsers'
6*/
7 
8/* ---------- RIGHT: config.h ---------- */
9#ifndef CONFIG_H
10#define CONFIG_H
11extern int maxUsers; /* declaration only */
12#endif
13 
14/* ---------- RIGHT: config.c ---------- */
15#include "config.h"
16int maxUsers = 100; /* the single definition */
17 
18/* ---------- main.c ---------- */
19#include <stdio.h>
20#include "config.h"
21 
22int main()
23{
24 printf("maxUsers = %d\n", maxUsers);
25 return 0;
26}
Output
maxUsers = 100

The Standard Header Pattern

Every real C project uses this shape. The header carries declarations; one .c file carries the definitions:

Example04
CCode Cell
1/* ---------- globals.h ---------- */
2#ifndef GLOBALS_H
3#define GLOBALS_H
4 
5extern int userCount; /* declarations */
6extern char appName[50];
7extern double taxRate;
8 
9void addUser(void); /* prototypes (extern is implied) */
10int getUserCount(void);
11 
12#endif
13 
14/* ---------- globals.c ---------- */
15#include <string.h>
16#include "globals.h"
17 
18int userCount = 0; /* definitions - exactly once */
19char appName[50] = "Inventory";
20double taxRate = 0.18;
21 
22void addUser(void) { userCount++; }
23int getUserCount(void) { return userCount; }
24 
25/* ---------- main.c ---------- */
26#include <stdio.h>
27#include "globals.h"
28 
29int main()
30{
31 printf("%s, tax %.0f%%\n", appName, taxRate * 100);
32 
33 addUser();
34 addUser();
35 printf("Users: %d\n", getUserCount());
36 return 0;
37}
Output
Inventory, tax 18%
Users: 2

extern on Functions Is Redundant

Functions already have external linkage, so all three of these declarations mean the same thing:

Example05
CCode Cell
1#include <stdio.h>
2 
3extern int add(int, int); /* explicit - and redundant */
4int subtract(int, int); /* identical meaning */
5extern int multiply(int a, int b);
6 
7int add(int a, int b) { return a + b; }
8int subtract(int a, int b) { return a - b; }
9int multiply(int a, int b) { return a * b; }
10 
11int main()
12{
13 printf("%d %d %d\n", add(6, 2), subtract(6, 2), multiply(6, 2));
14 return 0;
15}
Output
8 4 12

extern vs static — Opposites

At file scope the two keywords pull in opposite directions:

externstatic
LinkageExternal — sharedInternal — private
Other files can reach itYesNo
Allocates memoryNo — declaration onlyYes
Copies in the programOne, sharedOne per file
Use forGenuinely shared stateFile-private helpers
Example06
CCode Cell
1/* ---------- module.c ---------- */
2int publicValue = 10; /* shared with every file */
3static int privateValue = 20; /* this file only */
4 
5int getPrivate(void) { return privateValue; }
6 
7/* ---------- main.c ---------- */
8#include <stdio.h>
9 
10extern int publicValue;
11/* extern int privateValue; LINK ERROR: it is static in module.c */
12extern int getPrivate(void);
13 
14int main()
15{
16 printf("public = %d (direct access)\n", publicValue);
17 printf("private = %d (via a function)\n", getPrivate());
18 return 0;
19}
Output
public  = 10  (direct access)
private = 20  (via a function)

The Two Linker Errors

Almost every extern mistake produces one of these two messages. Knowing which is which saves a lot of time:

ErrorMeansFix
undefined reference to 'x'Declared but never definedDefine it in one .c, and compile that file
multiple definition of 'x'Defined more than onceRemove the initialiser from the header
Example07
CCode Cell
1/* CASE 1: undefined reference
2 main.c has: extern int missing;
3 printf("%d", missing);
4 No .c file defines it, or you forgot to compile the file that does.
5 -> undefined reference to 'missing'
6 Fix: add 'int missing = 0;' to one .c, and include it in the build.
7*/
8 
9/* CASE 2: multiple definition
10 shared.h has: int counter = 0; <- a DEFINITION in a header
11 Included by main.c and helper.c.
12 -> multiple definition of 'counter'
13 Fix: header gets 'extern int counter;'
14 one .c gets 'int counter = 0;'
15*/
16 
17/* CASE 3: it compiles but does not link
18 gcc main.c -o app <- data.c was never compiled
19 gcc main.c data.c -o app <- correct
20*/
Output
undefined reference to 'missing'
multiple definition of 'counter'

extern in the Same File

extern also works within one file — it lets you use a variable above its definition:

Example08
CCode Cell
1#include <stdio.h>
2 
3extern int definedLater; /* forward declaration */
4 
5void show(void)
6{
7 printf("definedLater = %d\n", definedLater);
8}
9 
10int definedLater = 42; /* the definition, further down */
11 
12int main()
13{
14 show();
15 return 0;
16}
Output
definedLater = 42

Types Must Match

The linker matches names, not types. Declare extern int count; in one file while the other defines long count; and it links happily — then reads the wrong number of bytes at run time, producing garbage that no compiler warning predicted. This is exactly why the extern declaration belongs in a header that both files include: then the compiler checks it for you.
Example09
CCode Cell
1/* ---------- data.c ---------- */
2long realValue = 1234567890123L;
3 
4/* ---------- BAD main.c : wrong type, no warning ----------
5extern int realValue; <- says int, actually a long
6printf("%d", realValue); <- reads 4 bytes of an 8-byte value
7*/
8 
9/* ---------- GOOD: shared.h ---------- */
10#ifndef SHARED_H
11#define SHARED_H
12extern long realValue; /* both files include this */
13#endif
14 
15/* data.c and main.c both #include "shared.h",
16 so any mismatch becomes a compile error, not a silent bug. */
Output
Always put extern declarations in a shared header

Common Mistakes

  • Defining instead of declaring in a headerint x = 5; in a .h gives "multiple definition".
  • Declaring but never defining — "undefined reference".
  • Forgetting to compile the defining file — same error, different cause.
  • Mismatched types across files — links cleanly, misbehaves at run time.
  • Trying to extern a static variable — internal linkage cannot be reached.
  • Missing include guards — the same header processed twice.
  • Overusing shared globals — every file that can change a value is a file you must read when it goes wrong.
Trainer's Note: the rule that prevents every one of these is one sentence — declare in the header, define in exactly one .c, and have every file include the header, including the one that defines it. That last part is what turns type mismatches into compile errors instead of run-time mysteries.
📝 Key Takeaways
  • extern says "this exists elsewhere" — it does not create storage.
  • Define a shared variable in exactly one .c file; declare it extern in a header.
  • extern int x; is a declaration; int x; at file scope is a definition.
  • Adding an initialiser makes extern a definition, defeating the purpose.
  • extern on a function is redundant — functions are external by default.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4