Using extern to Share Global Variables in C

In the C programming language, managing shared state across multiple source files requires a strict distinction between declaring a variable and defining it. This article explains how the extern keyword enables this separation, allowing distinct compilation units to reference the same global variable without causing multiple-definition errors during linking. By mastering the usage of extern, developers can safely expose global state across an entire project while maintaining clear ownership of data storage.

The Problem: Compilation Units and the Linker

A C program typically consists of multiple .c source files, each referred to as a translation unit or compilation unit. When compiling a project, the compiler processes each compilation unit independently into an intermediate object file (.o or .obj). Because the compiler operates on one unit at a time, it has no inherent knowledge of variables defined in other files.

If you place a standard global variable definition—such as int global_counter = 0;—inside a shared header file and include that header in multiple .c files, each compilation unit attempts to allocate storage for global_counter. While compilation will succeed, the linker will fail with a "multiple definition" or "duplicate symbol" error because it encounters two or more conflicting memory allocations with the same identifier.

Declarations vs. Definitions

Understanding extern requires understanding the difference between a declaration and a definition:

  • Definition: Instructs the compiler to reserve memory space for the variable. A variable can only be defined once across the entire linked program (abiding by the One Definition Rule).
  • Declaration: Informs the compiler about the variable's name and data type, but does not allocate memory. It asserts that the actual storage exists somewhere else.

The extern keyword converts a variable statement into a pure declaration (provided it is not given an initializer).

How extern Resolves Shared Variables

When you prefix a variable declaration with extern, you instruct the compiler not to allocate storage in the current translation unit. Instead, the compiler generates a symbolic reference and places an unresolved symbol entry in the generated object file.

During the final build step, the linker scans all object files to match these unresolved references with the single translation unit that contains the actual definition. Once matched, the linker replaces the unresolved references with the actual memory address of the defined variable.

Standard Implementation Pattern

The standard and safest way to share a global variable across multiple files involves three steps:

1. Declare the variable in a header file

Place an extern declaration inside a common header file. This makes the variable's name and type available to any file that includes the header.

/* counter.h */
#ifndef COUNTER_H
#define COUNTER_H

extern int shared_counter;

#endif

2. Define the variable in one source file

Allocate the actual storage in exactly one .c file. This file should also include the header to allow the compiler to verify that the definition matches the declaration.

/* counter.c */
#include "counter.h"

int shared_counter = 0; // Definition and memory allocation

3. Access the variable in other source files

Any other compilation unit can now include the header file and read or modify the variable directly.

/* main.c */
#include <stdio.h>
#include "counter.h"

int main(void) {
    shared_counter += 5;
    printf("Counter value: %d\n", shared_counter);
    return 0;
}

Important Considerations

  • Avoid Initializers with extern: Writing extern int x = 10; overrides the declaration status and turns the statement into a definition. Doing this in a header file reintroduces duplicate definition errors.
  • Default Linkage: Variables defined outside of any function have external linkage by default. Adding the static keyword restricts a global variable's scope exclusively to its own compilation unit, preventing extern from accessing it from other files.