Deep C Dives: Static Storage
Written by Mike James   
Tuesday, 20 May 2025
Article Index
Deep C Dives: Static Storage
Global Variables
Static v Heap

Global Variables

Is a file-level variable a global variable? In a sense it is, but there is also an important restriction. A file-level variable is only accessible to code in the same file. If you have a multi-file C project then code in other files cannot access the file-level variables in a given file.

This means that, for a single file C project, file-level variables are global variables.

What about multi-file projects?

Most non-trivial C projects use multiple.c and.h files. The compiler generally compiles each .c file, after combining it with any .h files included, in isolation to produce an intermediate .o file which the linker then puts together. Any file-level variables in a file are global within that file but inaccessible from other files. This is often a useful feature and can be used to provide a global variable without the risk that other files will use global variables that collide with it. If you want to share a global variable across files you need to use the extern keyword.

You can see that, unlike many languages, the file structure of a C project controls what is shared between the code in each of them. They are the units of isolation that allow a large project to be manageable. They are C’s encapsulation in object-oriented terms. See Dive 14, Structs and Objects.

Declaring and Defining

At this point it is helpful to draw a distinction between declaring a variable, i.e. informing the compiler that the variable exists, and defining a variable, i.e. getting the compiler to allocate storage for it.

If you declare a file level variable using extern then the compiler allows its use in the file as a global variable, but it doesn’t allocate storage for it. The linker expects to find a single definition of the variable in one of the files it is linking and it fills in the missing address in all of the files that make use of it.

The compiler converts each file or compilation unit into a possibly incomplete “object” file. Any variables that have been declared, but not defined, are included in the object file in a form that gives enough information for the linker to locate the variable in another file where it is defined. It is up to you to tell the linker which files to link together to create the complete program.

For example:

file1.c

int globalvar = 42;
int main(){…

As globalvar is declared and defined, it can be used within file1.c.

To use it in another file we need to use extern to just declare the variable:

file2.c

extern int globalvar;
functions that use globalvar

The extern informs the compiler that globalvar is defined in another file and that it should compile file2.c as if globalvar was an int but use a placeholder for its address. When the two files are linked to make the complete program, the placeholders for the address of globalvar are replaced by the actual address of globalvar as allocated in the compilation of file1.c.

If you plan to use global variables across multiple files then best practice is to put the extern declaration in a header file and make sure that this is included in all of the files that need access to the globals.

It is also worth knowing that extern can be used to make it clear that a function in one file is accessible from another. We tend not to use it because extern is the default for function declaration. If you want to make a function local to the compilation unit it is defined in then use the static modifier which we’ll look at next.

Cdive180

Static

Closely related to the idea of a file-level variable, is a static variable. A static variable can be declared anywhere in a program and it has the same lifetime as a file-level variable.

That is, a static variable lives for the entire duration of the program. The difference is that a static variable has the scope appropriate to where it has been declared. That is, if you create a static variable within a function, it exists before the function executes and continues to exist after the function has exited. This sounds like a global variable, but it is only accessible from within the function. This makes a static variable look like a local variable that lives for the life of the program.

Notice that you can declare a static variable in the main function and this looks a lot like a global variable, but one that only the main program can use directly. You can also declare a static variable at file level and in this case you cannot use it as a true global variable as it is restricted to the current compilation unit, i.e. the file it is declared in.

Typically a static variable is used to store state data that persists between function calls. For example:

#include <stdio.h>
#include <stdlib.h>
void myFunction(){
    static int myVar = 0;
    myVar++;
    printf("%d\n", myVar); 
}
int main(int argc, char **argv)
{
    myFunction();
    myFunction();
    myFunction();
    myFunction();
    return 0;
}

The only subtle point here is that the line:

static int myVar = 0;

is executed by the compiler in the sense that it reserves storage for myVar as part of the program file and zeros it. In this way the creation of the variable and its initialization occurs before the function is ever called and the program displays 1,2,3,4 as its result. Notice that myVar isn’t accessible from any other part of the program.

This is slightly different from automatic variables where lifetime and scope are related. In this case the compiler enforces the restriction by refusing access from outside the function that declares the static variable.

What do you use static variables for? The simple answer is that they are ideal for storing persistent state without using file-level or true globals. The data can also be shared with other parts of the program as it is safe to return a pointer to a static. For example, we can change the previous function to:

int *myFunction(){
    static int myVar = 0;
    myVar++;   
    return  &myVar; 
}

Notice that it now returns a pointer to the static myVar.

 

We can use this something like:

int main(int argc, char **argv)
{
    int *myLocalVar;
    myLocalVar=myFunction();
    printf("%d\n", *myLocalVar);
    *myLocalVar=42;
    myLocalVar=myFunction();
    printf("%d\n", *myLocalVar);
    return 0;
}

This works and the static variable is changed by the main program.

Is this a good idea?

It is safe in the sense that the variable is allocated at a fixed location and always exists. The only downside is that the function is not re-entrant, i.e. if another thread calls it while it is being used then the static variable is not reallocated. Compare this to a function which only uses automatic local variables and allocates any other memory on the heap. Such a function is by default re-entrant.

It may be safe, but it is difficult to implement a scheme that can control the access to the static variable. One possibility is to use access functions to hide the implementation of the state representation from its clients.

It is also worth noting that there is nothing to specify how static variables are to be implemented. However, implementing them as file-level variables with restricted access is so easy, it is difficult to think of a reason why a compiler would do anything else.



Last Updated ( Tuesday, 20 May 2025 )