A real C project is usually more than one source file. One file may hold main, another may hold helper functions, and different people may write different files. In that situation a global variable is visible to every file in the project, which is often more access than you want.

static gives you a way to keep a global variable private to the file that defines it. This note builds a small two-file project to show the problem, the usual extern fix, and why static is sometimes the safer choice.

The project

Make a folder and put two source files in it.

static_1/
├── main.c
└── file1.c

Start with an empty function in file1.c.

// file1.c
void file1_myFun1(void) {
}

In main.c, create a global variable called PrivateData, print it, and then call the function from the other file.

// main.c
#include <stdio.h>
 
int PrivateData = 100;
 
int main() {
	printf("Before the call: %d\n", PrivateData);
	file1_myFun1();
	printf("After the call:  %d\n", PrivateData);
	return 0;
}

The name PrivateData is only a name. Nothing stops another file from changing the variable yet. That is exactly what we are going to test.

First compile: the missing prototype

Compile both files together.

gcc main.c file1.c -o main
main.c: In function 'main':
main.c:7:9: warning: implicit declaration of function 'file1_myFun1'

The compiler is reading main.c on its own. When it reaches the call to file1_myFun1, it has not seen that function anywhere, so it does not know what arguments it takes or what it returns. The line

void file1_myFun1(void);

fixes that. It is a prototype, a promise to the compiler that the function is defined in some other file. After adding it, main.c compiles cleanly. The call and the body now agree.

Note

A prototype and a definition are not the same thing. The prototype only describes the function. The body in file1.c is the definition that actually does the work. Declaration vs Definition explains the difference for variables.

Second compile: the other file cannot see the variable

Now let file1.c change the value. The function tries to assign 900 to PrivateData, and a second printf in main.c will show whether the change reached main.c.

// file1.c
void file1_myFun1(void) {
	PrivateData = 900;
}
// main.c
#include <stdio.h>
 
int PrivateData = 100;
 
void file1_myFun1(void);
 
int main() {
	printf("Before the call: %d\n", PrivateData);
	file1_myFun1();
	printf("After the call:  %d\n", PrivateData);
	return 0;
}
gcc main.c file1.c -o main
file1.c: In function 'file1_myFun1':
file1.c:2:2: error: 'PrivateData' undeclared (first use in this function)

Each source file is compiled separately. The compiler working on file1.c has never seen the line int PrivateData = 100;, because that line is in main.c. As far as this file knows, the name does not exist.

Telling file1.c that the variable exists

The variable is already defined in main.c. We do not want a second copy, so file1.c needs a declaration, not a definition. The keyword for that is extern.

// file1.c
extern int PrivateData;
 
void file1_myFun1(void) {
	PrivateData = 900;
}

extern int PrivateData; says that a variable with this name and type exists somewhere else in the project. No memory is set aside here. The linker connects this name to the one definition in main.c.

Compile and run.

gcc main.c file1.c -o main
./main
Before the call: 100
After the call:  900

Follow what happened, step by step.

  1. The program starts. main.c has already set aside storage for PrivateData and put 100 in it.
  2. main prints 100.
  3. main calls file1_myFun1.
  4. Inside that function, the extern line does not create anything. It only lets the name PrivateData refer to the storage that main.c created.
  5. PrivateData = 900; writes 900 into that shared storage.
  6. The function returns.
  7. main prints the same storage again and sees 900.

The variable was changed from another file, without main.c ever agreeing to it.

The problem

The name PrivateData was chosen because main.c wants the variable to be private. It expects that no other file will touch it. But a plain global variable is public to the whole project, so any file can declare it with extern and write to it.

That is fine when the variable really is shared. It is risky when only one file should own it. One careless assignment in another file can break the logic in main.c, and the bug will be hard to find because the two lines are in different files.

The static fix

Put static in front of the global variable in main.c.

// main.c
#include <stdio.h>
 
static int PrivateData = 100;
 
void file1_myFun1(void);
 
int main() {
	printf("Before the call: %d\n", PrivateData);
	file1_myFun1();
	printf("After the call:  %d\n", PrivateData);
	return 0;
}

Leave file1.c exactly as it was, with its extern declaration and its assignment. Then compile again.

gcc main.c file1.c -o main
/usr/bin/ld: /tmp/ccQ7k3zA.o: in function 'file1_myFun1':
file1.c:(.text+0x0): undefined reference to 'PrivateData'
collect2: error: ld returned 1 exit status

Both files compiled without complaint. The error comes from the linker, which is the program that joins the compiled pieces together. file1.c still asks for a variable named PrivateData. But the only definition in the project is now a static one inside main.c, and a static definition is not offered to other files. The linker searches, finds nothing it is allowed to use, and stops.

The assignment is now impossible. main.c keeps its private data, and the project does not build until the other file stops reaching for it.

Important

A global variable marked static is private to its own source file. Other files cannot reach it, even with extern.

To make the project compile again, remove the extern declaration and the assignment from file1.c and give the function something else to do, or remove the call to it. A static variable cannot be externed.

What changed

Plain global in main.cStatic global in main.c
Can main.c use the nameYesYes
Can file1.c use the nameYes, with externNo
Where the storage livesOne copy for the whole programOne copy, private to main.c
Starts at0 if not initialized0 if not initialized

The lifetime does not change. In both columns the variable is created before main starts and lives until the program ends. Only the visibility changes. That is the whole point of the keyword here.

Two jobs for one keyword

The word static now has two separate uses, and the position tells you which one you have.

Where you write staticWhat it does
On a variable inside a functionKeeps the value between calls, name still local to the function
On a variable outside every functionKeeps the name private to the source file
On a functionKeeps the function name private to the source file

The first row is the one covered in Storage Classes. The second row is the one this note just showed. The third row follows the same idea as the second, but for functions instead of variables. Static Functions covers it.

Note

Two different files may each have their own static int PrivateData; without a clash. Each one is private to its file, so the linker never has to choose between them. That also means the two variables are separate, and changing one does not change the other.

When to use it

Use static on a global when the variable is only the business of one file. It keeps the project tidy and stops other files from depending on details they should not know about.

Use a plain global, together with an extern declaration in the other files, when the variable really is shared by design. Extern Keyword explains that keyword in detail.

Previous: Storage Classes