Question
I want to understand how the extern keyword is used in C to share variables between source files.
I know that global variables sometimes appear with the extern keyword, but I am not sure what an extern variable actually is. Specifically:
- What does
externmean for a variable? - What does the declaration look like?
- What scope does such a variable have?
- How does sharing a variable across multiple
.cfiles work in practice? - In which file should
externbe used, and where should the variable actually be created?
For example, I am trying to understand the pattern behind code like this:
/* file1.c */
int counter = 0;
/* file2.c */
extern int counter;
How does this work precisely in C?
Short Answer
By the end of this page, you will understand the difference between declaring and defining a global variable in C, what extern means, how scope and linkage affect shared variables, and how to correctly share one variable across multiple source files without causing linker errors.
Concept
In C, extern is mainly used to say:
"This variable exists somewhere else. I want to use it here, but I am not creating storage for it in this file."
That idea is the key to understanding extern.
Declaration vs definition
A global variable can appear in two important ways:
- Definition: actually creates the variable and allocates storage for it.
- Declaration: tells the compiler the variable exists and what its type is.
Example:
int counter = 0; // definition
extern int counter; // declaration
The definition creates one real counter variable in the program. The extern declaration does not create a second variable. It only allows another source file to refer to the existing one.
Why this matters
C programs are often split into multiple .c files. Each source file is compiled separately, so the compiler needs to know the types of names used in that file. If one file wants to use a global variable defined in another file, it needs a declaration. That is what extern is for.
Scope vs linkage
Beginners often confuse scope and linkage.
- Scope answers: where in the code can this name be used?
- Linkage answers: does this name refer to the same object across different files?
A file-scope variable like this:
int counter = 0;
has:
- file scope: from its declaration to the end of the file
- external linkage by default: other files can refer to the same variable using
extern
A declaration like this:
extern int counter;
also has file scope if written outside a function, but it does not define storage.
The common pattern
Usually you do this:
In a header file
/* globals.h */
extern int counter;
In exactly one source file
/* globals.c */
int counter = 0;
In any file that uses it
/* main.c */
#include "globals.h"
int main(void) {
counter++;
return 0;
}
This works because:
- the header gives every file the declaration
- one
.cfile gives the program the actual definition - the linker connects all references to the same variable
Important rule
If you define the variable in more than one source file, you usually get a multiple definition linker error.
If you declare it with extern everywhere but never define it anywhere, you usually get an undefined reference linker error.
So the rule is:
- declare with
externin headers or other files - define once in exactly one
.cfile
Mental Model
Think of a shared global variable like a company storage locker.
- The definition is the actual locker built in one location.
- An
externdeclaration is a note telling people: "The locker exists in building A; use that one." - If every building creates its own locker with the same name, there is confusion.
- If everyone only has notes but no locker was ever built, nothing can be stored.
So:
- one real locker = one definition
- many references to that locker =
externdeclarations
This is why extern is about referring to existing storage, not creating new storage.
Syntax and Examples
Core syntax
Define a global variable
int counter = 0;
This creates the variable.
Declare a global variable from another file
extern int counter;
This says the variable exists somewhere else.
Basic multi-file example
globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
extern int counter;
#endif
globals.c
#include "globals.h"
int counter = 0;
main.c
#include <stdio.h>
#include "globals.h"
int main(void) {
printf("Before: %d\n", counter);
counter += 5;
printf("After: %d\n", counter);
return 0;
}
What this does
globals.htells other files thatcounterexists.globals.ccreates the actual variable.main.cuses that variable.
All files refer to the same counter.
Inside a function
You can also write:
void print_counter(void) {
extern int counter;
printf("%d\n", counter);
}
This is valid, but less common. In real code, developers usually put the declaration in a header and include it where needed.
extern with functions
Functions also have external linkage by default, so this is similar:
int add(int a, int b);
You could write:
extern int add(int a, int b);
but for functions, extern is usually omitted because it is implied.
Step by Step Execution
Consider these files.
globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
extern int counter;
#endif
globals.c
#include "globals.h"
int counter = 10;
main.c
#include <stdio.h>
#include "globals.h"
int main(void) {
printf("%d\n", counter);
counter++;
printf("%d\n", counter);
return 0;
}
Step-by-step
1. globals.h is included in main.c
The compiler sees:
extern int counter;
So it learns:
- there is an
intnamedcounter - this file may use it
- storage is not created here
2. globals.c is compiled
The compiler sees:
int counter = 10;
This creates the real global variable.
3. main.c is compiled
main.c uses counter in printf and counter++. That is allowed because the compiler already saw its declaration from the header.
4. Linking happens
The linker combines the compiled object files.
main.crefers to a variable namedcounterglobals.cprovides the definition ofcounter
The linker connects them.
5. Program runs
Initial value:
counter == 10
Then this line runs:
counter++;
Now:
counter == 11
Output:
10
11
Key takeaway
Compilation checks types and names within each file. Linking connects declarations to definitions across files.
Real World Use Cases
When shared globals are used
Although global variables should be used carefully, they do appear in real C programs.
Configuration flags
extern int debug_enabled;
Used by multiple source files to enable extra logging.
Program state counters
extern unsigned long request_count;
Useful in small tools or embedded systems to count events.
Hardware register mappings in embedded C
A shared global may represent device state or a system-wide handle used across modules.
Shared error/status values in legacy code
Older codebases sometimes expose shared status variables accessible from several files.
Better alternatives in many cases
In modern C code, developers often prefer:
- passing values through function parameters
- keeping state inside a
struct - limiting visibility with
static - exposing accessor functions instead of raw globals
These approaches reduce coupling and make code easier to test.
Real Codebase Usage
In real projects, extern is usually part of a small, repeatable pattern.
Common pattern: declaration in header, definition in source
app_state.h
#ifndef APP_STATE_H
#define APP_STATE_H
extern int app_mode;
#endif
app_state.c
#include "app_state.h"
int app_mode = 1;
This is the standard way to expose one global object to multiple files.
Common pattern: hide most globals with static
Developers often avoid exposing variables unless necessary.
static int internal_cache_size = 128;
This variable can only be used inside its own .c file. That is often safer.
Access through functions
Instead of exposing a writable global directly, a codebase may do this:
/* config.h */
int get_log_level(void);
void set_log_level(int level);
/* config.c */
static int log_level = 0;
int get_log_level(void) {
return log_level;
}
void set_log_level(int level) {
log_level = level;
}
This gives more control and allows validation.
Validation and guard clauses
When globals affect program behavior, developers often combine them with checks:
extern int max_connections;
int open_connection(void) {
if (max_connections <= 0) {
return -1;
}
return 0;
}
Why teams are cautious
Shared global state can make code harder to reason about because any file may change it. extern is useful, but many real codebases keep its usage limited.
Common Mistakes
1. Defining the variable in a header
This is a very common mistake.
Broken code
/* globals.h */
int counter = 0;
If several .c files include this header, each file gets its own definition, causing linker errors.
Correct version
/* globals.h */
extern int counter;
/* globals.c */
int counter = 0;
2. Using extern but never defining the variable
Broken code
/* globals.h */
extern int counter;
If no source file contains:
int counter;
then the linker cannot find the real variable.
3. Confusing scope with linkage
Beginners may think extern changes scope everywhere automatically. It does not.
void f(void) {
extern int counter;
counter++;
}
Here, the name counter is visible only inside f, but it still refers to a variable defined elsewhere.
4. Mixing static and extern
Broken idea
/* file1.c */
static int counter = 0;
/* file2.c */
extern int counter;
This does not work for sharing.
Why?
staticat file scope gives internal linkage- the name is private to
file1.c file2.ccannot access it
5. Assuming extern creates storage
This is false for normal variable declarations.
extern int counter;
This declares only. It does not allocate storage.
6. Forgetting to include the shared header
If one file manually writes a different declaration, types can drift.
Risky
extern long counter;
while the real definition is:
int counter = 0;
Type mismatches across files can cause undefined behavior or linker/compiler problems. Use one shared header to avoid this.
Comparisons
extern declaration vs definition
| Form | Example | Creates storage? | Typical use |
|---|---|---|---|
| Definition | int counter = 0; | Yes | Create the variable once |
Declaration with extern | extern int counter; | No | Refer to variable defined elsewhere |
External linkage vs internal linkage
| Keyword/form | Example | Visible from other files? | Notes |
|---|---|---|---|
| Default global definition | int counter = 0; | Yes | External linkage by default |
| File-private global | static int counter = 0; | No | Internal linkage |
| Reference to external variable | extern int counter; | Refers to external name | Does not define storage |
extern vs static
| Concept | extern | static at file scope |
|---|---|---|
| Main purpose | Refer to a variable defined elsewhere | Restrict visibility to one source file |
| Storage created? | No, not by itself | Yes, if it is a definition |
| Cross-file access | Yes | No |
Global variable vs function parameter
| Approach | Pros | Cons |
|---|---|---|
Shared global with extern | Easy to access from many files | Tight coupling, harder to test |
| Pass as parameter | Clear data flow, easier to test | More code to pass state around |
Store in a struct | Groups related state cleanly | Requires slightly more design |
Cheat Sheet
Quick rules
externusually means: this variable is defined somewhere else- Use
externfor declarations, not for creating the variable - Define the variable once in one
.cfile - Put the
externdeclaration in a header if multiple files need it - Include that header wherever the variable is used
Common pattern
/* globals.h */
extern int counter;
/* globals.c */
int counter = 0;
/* main.c */
#include "globals.h"
counter++;
Remember
int counter = 0;→ definitionextern int counter;→ declarationstatic int counter = 0;→ private to that source file
Linkage summary
- file-scope
int counter;→ usually external linkage - file-scope
static int counter;→ internal linkage - file-scope
extern int counter;→ declaration of external name
Common errors
Multiple definition
Caused by defining the same global in more than one .c file, or in a header included by multiple files.
Undefined reference
Caused by declaring with extern but never providing one real definition.
Best practice
- Prefer fewer globals
- If a global must be shared, expose it through one header and define it once
- Consider
staticor accessor functions when possible
FAQ
What does extern mean in C?
It means the variable or function is declared here but defined somewhere else, usually in another source file.
Does extern create a variable?
No. A declaration like extern int x; does not create storage for x. It only tells the compiler that x exists.
Where should I put extern declarations?
Usually in a header file, so every source file that needs the variable can include the same declaration.
Where should I define the actual global variable?
In exactly one .c file.
What is the difference between extern int x; and int x;?
extern int x; is a declaration only. int x; at file scope is typically a definition.
Can I use extern inside a function?
Yes. It declares that the name refers to an external variable, but this is less common than putting the declaration in a header.
Can extern access a static variable from another file?
No. A static file-scope variable has internal linkage and is private to its own source file.
Is using global variables with extern good practice?
Sometimes it is acceptable, especially in small or embedded programs, but overusing globals can make code harder to maintain and test.
Mini Project
Description
Build a small multi-file C program that keeps track of how many tasks have been completed. The counter should be shared across source files using extern. This demonstrates the correct pattern for declaring a shared global in a header and defining it in exactly one .c file.
Goal
Create a program where multiple source files read and update the same global counter variable.
Requirements
- Create a header file that declares a shared variable with
extern. - Define the shared variable in exactly one source file.
- Use one source file to increase the counter.
- Use another source file to print the counter.
- Confirm that both files access the same variable.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.