A storage class decides three things about a variable:

  • Scope: the part of the program where the variable name can be used.
  • Visibility: which functions or files can reach the variable.
  • Lifetime: how long the memory for the variable stays alive.

Scope and visibility sound close, and in small programs they often feel the same. The difference shows up when a variable lives for the whole program but only one function is allowed to use its name.

In C you change these three things with a storage class specifier. The two common ones are static and extern. This note uses static to solve a problem you have already met: a local variable that loses its value as soon as its function returns.

The problem: a counter that keeps starting over

We want a function that prints how many times it has been called. Here is the first attempt.

#include <stdio.h>
 
void myFun1(void);
 
int main() {
	myFun1();
	myFun1();
	myFun1();
	myFun1();
	return 0;
}
 
void myFun1(void) {
	int count = 0;
 
	count = count + 1;
 
	printf("This function executed %d times\n", count);
}
This function executed 1 times
This function executed 1 times
This function executed 1 times
This function executed 1 times

The count never grows. Let us follow one call from start to finish.

  1. main starts and reaches the first call to myFun1.
  2. Control jumps into myFun1.
  3. The line int count = 0; makes a local variable and puts 0 in it.
  4. count = count + 1; reads 0, adds 1, and stores 1 back into count.
  5. printf prints 1.
  6. The function returns to main.
  7. The local variable dies. Its memory is released and its value is lost.

Now the second call begins. Step 3 runs again, so a brand new count is made and set to 0. The old value of 1 is gone. That is why every call prints 1.

Callcount at the startAfter adding 1Printed
1011
2011
3011
4011

Important

A local variable is created when its function starts and destroyed when the function returns. Its value does not survive the return.

First fix: move the variable to global scope

One way to keep count alive is to move it outside every function. A global variable is created before main starts and lives until the program ends.

#include <stdio.h>
 
int count = 0;
 
void myFun1(void);
 
int main() {
	myFun1();
	myFun1();
	myFun1();
	myFun1();
	return 0;
}
 
void myFun1(void) {
	count = count + 1;
 
	printf("This function executed %d times\n", count);
}
This function executed 1 times
This function executed 2 times
This function executed 3 times
This function executed 4 times

Now the steps look like this.

  1. Before main starts, count is created and set to 0.
  2. First call: count becomes 1 and prints 1.
  3. myFun1 returns, but count does not die.
  4. Second call: count is still 1, so it becomes 2 and prints 2.
  5. Third call prints 3.
  6. Fourth call prints 4.

The value is kept because the variable is never destroyed between calls. The global variable lives for the whole run of the program.

The problem with the global fix

The global variable is now public. Every function in the file can see it and change it.

void resetCount(void) {
	count = 0;
}

One call to resetCount and the count is back to zero, even though myFun1 was called many times. Another file in the same project can also reach count by declaring it with extern, so the variable is not private to this file either.

That is too much access. We want a variable with the long life of a global, but with a name that only myFun1 can use. In other words, we want a private variable that does not die when the function returns.

The static fix

Put the keyword static in front of the local variable.

#include <stdio.h>
 
void myFun1(void);
 
int main() {
	myFun1();
	myFun1();
	myFun1();
	myFun1();
	return 0;
}
 
void myFun1(void) {
	static int count = 0;
 
	count = count + 1;
 
	printf("This function executed %d times\n", count);
}
This function executed 1 times
This function executed 2 times
This function executed 3 times
This function executed 4 times

The output is correct, and now only myFun1 can use the name count. Try to change it from main and the compiler stops you.

int main() {
	count = 100;
	myFun1();
	return 0;
}
error: 'count' undeclared (first use in this function)

The name count is not in scope in main, so the compiler cannot connect that line to the variable inside myFun1.

Here is what happens on each call.

  1. First call: storage for count is already set aside, and it holds 0. The initialization with 0 has already run. count becomes 1 and printf prints 1.
  2. myFun1 returns. The variable stays alive with the value 1.
  3. Second call: the line static int count = 0; is not run again. The variable still holds 1. It becomes 2 and prints 2.
  4. Third call: it still holds 2, becomes 3 and prints 3.
  5. Fourth call: it still holds 3, becomes 4 and prints 4.

The initialization with 0 happens only once. Later calls skip it and use the value left from the previous call.

Important

A static local variable is created once and lives until the program ends. Its name is visible only inside the function where it is defined.

You may hear this described as a global variable that is private to a function. That is a useful way to remember it. The storage lasts like a global, but the name stays inside the function.

What happens if you leave out the value

A static variable starts at 0 even when you do not initialize it. Inside myFun1, you can write this and get the same counting behavior:

void myFun1(void) {
	static int count;
 
	count = count + 1;
 
	printf("This function executed %d times\n", count);
}

This is safe. The compiler sets it to 0 before the program starts.

That is different from an ordinary local variable, which starts with whatever garbage happened to be in that memory. See Variable scope in C for that problem.

Local, global and static local together

Local variableGlobal variableStatic local variable
Where it is definedInside a functionOutside every functionInside a function, with static
Who can use the nameOnly that functionEvery function in the file, and other files through externOnly that function
How long it livesUntil the function returnsUntil the program endsUntil the program ends
Starting valueGarbage00
When storage is set asideEach time the function is calledOnce, before main startsOnce, before the program starts

The key row is the lifetime row. A static local keeps its value for the same reason a global does. The compiler gives it a fixed place in memory that is not reused when the function returns. Only the name is kept private.

The other common specifier: extern

extern is the second storage class specifier you will see often. It does not create a new variable. It tells the compiler that the variable is defined somewhere else, usually in another file.

You have already seen it fail when the other file was missing. That example is in Declaration vs Definition.

Note

C also has auto and register. auto is the default for a local variable, so writing it changes nothing. register asks the compiler to keep a variable in a very fast storage place inside the CPU, and most modern compilers treat that as a hint. Neither one is needed for the examples here.

One more thing about static

The word static has a second job outside functions. A global variable or function marked static becomes private to its own file. That is a different use from the one in this note, and the next lecture covers it.

Previous: Address of a variable