Effectively Use extern To Share Global Variables

Updated:

In another Dev Tip, we see in detail what it means to declare and define a variable. Recalling the key concepts can be helpful to better understand what we are talking about here.

  • A declaration is just about informing the compiler that a variable or a function exists, with that name and of that type.
  • A definition is where the storage gets actually allocated for variables and where the functions get implemented.
  • You can have many declarations of the same thing, but only one definition.

The best way to share global variables

The cleanest and most reliable way to export a global variable is to put an extern declaration inside a header file. This way, when a source file wants to use the interface provided by that header file, it will include it, and by including it, it also gains the extern declaration of that variable. This way, it will be able to reference that global variable because it knows its declaration.

It is worth mentioning that the include operation is like a copy and paste operation performed by the preprocessor before the actual compilation begins. So, it’s important to use header files and use them correctly because they allow the compiler to perform cross-check operations between the different translation units, which are compiled independently.

Good practices to remember

  • You can only put extern declarations of variables into header files, no variable definitions (static or unqualified).
  • Achieve the Single Point of Truth: only one header file declares a given variable.
  • Never put extern declaration inside source files. A source file should include a header declaring that variable.
  • An exported global variable can only be defined once in the whole program.
  • Any declared variable has to be defined.
  • The source file defining an exported global variable should also include the header file declaring it, to ensure consistency.
  • Don’t make extern declarations of variables inside functions.

And the golden rule:

  • Try to avoid using shared global variables! Better to use static variables, even better if with function scope.

Modularized example

Let’s make an ultra minimalistic example of a modular program using a shared global variable. Here we have main.c, module_1.c, module_1.h, module_2.c, module_2.h and we want to share a variable named gvar between all of them.

module_1.h

extern int gvar;   /* Declaration of the global variable */
int update_gvar(); /* Declaration of the function update_gvar */

module_1.c

#include "module_1.h"

int gvar; /* Definition of the global variable */

int update_gvar() { /* Definition of update_gvar */
    return gvar--;
}

module_2.h

void display_gvar(); /* Declaration of display_gvar */

module_2.c

#include <stdio.h>
#include "module_1.h"
#include "module_2.h"

void display_gvar() { /* Definition of display_gvar */
    printf("gvar: %d\n", gvar); /* We can use gvar thanks to the inclusion of module_1.h */
}

main.c

#include "module_1.h"
#include "module_2.h"

int main() {
    gvar = 100; /* Set the initial value */
    while (update_gvar() > 0) {
        display_gvar();
    }
}