Static Variables in C and C++ – File Level

When is a global not a global? When it’s a static variable.

This post, and the next three, will talk about static variables. Let’s start with static variables declared in a file.

Static variables in a file

If you declare a static variable at file level (i.e. not inside any other code), then you are creating a so-called “global” variable that will:

  1. be available for the entire duration of your program, and
  2. be accessible only from that translation (compilation) unit (i.e. the file itself and any file that includes it).

Number two is the important one here. It means that if you include (say) a header that contains a static variable in two different source files, you will end up with two “global” variables with the same name.

What is a translation unit?

Well, it’s roughly the collection of code that is passed to the compiler after preprocessing. i.e. it’s a source file (.c or .cpp), and all its includes. When you compile a single file, such as main.cpp, you only have one translation unit. But when you compile more than one .c or .cpp file, you have multiple translation units. A static variable is only available to a single translation unit. If you include the same variable in another unit, you will effectively have two variables with the same name.

An example will explain it more succinctly.

Code example 1 – single translation unit

#include <iostream>  

static int storage = 0;

int main()
{
    std::cout << "Storage: " << storage << " TB" << std::endl;

    //e.g. add disk drive and increase storage
    storage += 1024;

    //e.g. add disk drive and increase storage
    storage += 1024;

    std::cout << "Storage: " << storage << " TB" << std::endl;
}

The output from this is:

Storage: 0 TB
Storage: 2048 TB

This is nice and simple. You can see we’ve declared and initialised the static variable at the top of the file. We increment it in the code, and then we output that variable to see that it has changed accordingly. You can access this variable from anywhere in this file. If you defined functions here, they would also be able to see and share the static variable.

Now let’s look at another example.

Code example 2 – two translation units

This example has four files, main.cpp, Storage.h, DiskDrive.cpp and DiskDrive.h

Compile it with:

g++ main.cpp DiskDrive.cpp

File 1: main.cpp

#include <iostream>
#include "DiskDrive.h"
#include "Storage.h"

int main()
{
    std::cout << "Storage: " << storage << " TB" << std::endl;

    DiskDrive d1;
    DiskDrive d2;

    d1.Initialise();
    storage += 1024;

    d2.Initialise();
    storage += 1024;

    std::cout << "Storage: " << storage << " TB" << std::endl;
    std::cout << "Storage: " << d2.GetSystemStorageTotal();
    std::cout << " TB" << std::endl;
}

File 2: Storage.h

static int storage = 0;

File 3: DiskDrive.cpp

#include <iostream>
#include "DiskDrive.h"
#include "Storage.h"

//constructor
DiskDrive::DiskDrive() {}

//destructor
DiskDrive::~DiskDrive(){}

void DiskDrive::Initialise()
{
    //do setup
}

int DiskDrive::GetSystemStorageTotal()
{
    return storage; //the static "global" variable
}

File 4: DiskDrive.h

class DiskDrive
{
public:
    DiskDrive();
    ~DiskDrive();
    void Initialise();
    int GetSystemStorageTotal();
};

The output of this program is as follows:

Storage: 0 TB
Storage: 2048 TB
Storage: 0 TB

As you can see, the storage total output by the DiskDrive object is zero (output line 3).

Why?

It has a value of zero because DiskDrive.cpp creates a new translation unit that includes the static variable. So now we have two static variables in our program, both called storage, one in each translation unit.

Funny, huh?

Next time we’ll look at static variables declared inside functions.