static in depth

After my previous post about static members in various programming language, I decide to write a post about the word static in depth. static is a key word in multiple programming languages, with multiple distinct but related meanings.

The history

The static keyword in C originally served as a “storage specifier”. Although C is a high level language, it is designed for system programming which allows fine-grained control of memory, and the language constructs can be easily mapped to system infrastructure (hence called “high-level assembler”). Therefore, we need to know how objects are stored in the memory. Objects in C have one of the following storage duration:

  • Automatic storage duration: The object comes to existence automatically when it comes to the scope, and disappears when it leaves the scope.
  • Static storage duration: The object exists for the whole lifetime of the program.
  • Dynamic storage duration: The object is created and destroyed manually by the programmer.

In terms of implementation, the three kinds of storage duration maps perfectly to three parts of the memory:

  • The stack: When a scope is entered, the stack pointer is decreased to allocate space for objects of automatic storage duration automatically. Therefore, these objects are stored on the stack.
  • The program image: Objects with static storage duration is stored inside the program image (normally the data section), and is loaded and available once the program starts executing.
  • The heap: malloc() allocates memory from the heap, which is used by objects with dynamic storage duration.

The keyword auto or static are used to specify an object having automatic or static storage duration respectively, while objects with dynamic storage duration are created with malloc() and destroyed with free() respectively. Because all objects declared in functions have automatic storage duration by default, the auto is basically useless in C, and replaced with another completely unrelated meaning in C++11. However, objects declared in the file scope always have static storage duration because they always exists when the program is running.

static may be applied to objects declared to file scope as well, however, it has a different meaning here. A static object declared in file scope means that it has internal linkage, i.e. it can only be referenced inside the same translation unit (i.e. the file with all the included stuff), and invisible to other translation units, where a file-scope object declared without static can be referenced by other translation units by means of extern declaration. To summarise, objects declared in file scope can be:

  • without linkage specifier: declared and defined with external linkage
  • with extern: declared to reference a definition elsewhere
  • with static: declared and defined with internal linkage

Therefore, static in C has two usages:

  • in file scope: to declare an object with internal linkage rather than external linkage
  • in function scope: to declare an object with static storage duration rather than automatic storage duration

Derived meaning of static

Use in function scope

The file scope meaning of static in C is normally not relevant in most other programming languages because it deals with linkage, which is an implementation detail of the C programming language. However, the meaning in function scope has some interesting properties, which forms the basis of static in other programming languages.

Consider the following C program:

#include <stdio.h>

int test(void) {
    static int count = 0;
    ++count;
    return count;
}

int main(void) {
    for (int i = 0; i < 10; ++i) {
        printf("%d\n", test());
    }
    return 0;
}

Inside function test, count is declared to have static duration duration, which means the object exists throughout the whole program lifetime, i.e. “shared” and “preserved” between function invocations. Therefore, count effectively acts as a counter which keeps track how many times the function is called. Using static variables inside function is therefore considered bad programming practice because it introduces hidden global state of the program, just like using global variables. (The common thing between a static local variable and a global variable is that, they exist throughout the program runtime. These variables are shared across the whole program, which are hard to test and break thread safety).

Therefore, the static keyword is adopted by other programming languages to declare variables which should be preserved and shared across function invocations.

Class scope

In C++, apart from usage in file and function scope, static can also be used to declare class members.

static member objects

If static is used to declare an object member, that object member become having static storage duration, rather than contained inside the class object inheriting the storage duration. A static object member is not defined at the point of declaration. A definition must be provided in namespace scope outside the class, mainly because the class declaration is normally included in multiple translation units, but the definition can only appear once in the whole program.

Consider the following C++ program:

#include <iostream>

using std::cout;

struct Test {
    static int count;
    Test() {
        ++count;
    }
};

int Test::count;

int main() {
    Test a, b;
    cout << a.count << '\n';
}

In the above program, count is declared as static inside Test, which means it has static storage duration, existing for the whole program lifetime, i.e. shared and preserved across class instances, same as the effect of static on variables in function scope. Being “shared across class instances” can be reworded to “not depending on a particular class instance”, unlike regular members. Therefore, a.count can be replaced with Test::count in the above code, removing the reference to an instance. In particular, a.count is considered bad practice because it can produce unexpected effects when inheritance is involved (more details in my previous article) and we should always use a class expression to access static members.

Furthermore, using static member objects is also considered bad practice, because it is just another form of global variable (especially public static member objects) shared across the whole program.

static member functions

The sense that a static member “does not depend on a particular class instance” is used for member function (i.e. method) as well. If a member function is declared with static, it can be called without an instance, leading to the limitation that a static member function

  • cannot use this
  • cannot use non-static members directly

Although the term “storage duration” does not apply to functions (functions are not objects) in C++, the meaning of static when applied to a class method traces its root to the usage of C to declare a static variable, which is shared across the whole program.

static member classes

In Java, even a member class can be declared static as well!

A class can have classes as members (called inner classes) in Java, where instances of non-static inner classes can only exist inside an instance of its outer class. In implementation terms, an instance of an inner class holds an implicit pointer to the outer class as OuterClass.this, and you can access all instance members of the outer class inside the inner class implicitly.

Here is an example, in Java, how a non-static inner class is used:

class Test {
    public class InnerClass {
        public void increaseOuterX() {
            ++x;
        }
        public Test getOuterClass() {
            return Test.this;
        }
    }

    public int x;
}

public class Main {
    public static void main(String[] args) {
        Test.InnerClass a = new Test().new InnerClass();
        Test.InnerClass b = new Test().new InnerClass();
        Test.InnerClass c = b.getOuterClass().new InnerClass();
        a.increaseOuterX();
        b.increaseOuterX();
        c.increaseOuterX();
        System.out.println(a.getOuterClass().x);
        System.out.println(b.getOuterClass().x);
        System.out.println(c.getOuterClass().x);
    }
}

In the main method, b and c are inner class objects inside the same outer class object, and a is inside another outer class object. Note that the InnerClass objects are created inside an OuterClass objects, and getOuterClass() returns the containing outer class instance of the inner class object.

By declaring the inner class static, it no longer depends on an outer class instance, i.e. OuterClass.this can no longer be used. In such case, the inner class instances can and must be created independently without an outer class instance, and the inner class can no longer access non-static members of the containing class, just like a static member function. However, a non-static member class cannot contain a static member because it creates confusion. Should static members of a non-static inner class shared among inner class instances created from different outer class instances, or shared among inner class instances created from the same outer class instance only? For example, the following is illegal:

class Test {
    public class InnerClass {
        public static int innerStaticMember;
        public Test getOuterClass() {
            return Test.this;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Test.InnerClass a = new Test().new InnerClass();
        Test.InnerClass b = new Test().new InnerClass();
        Test.InnerClass c = b.getOuterClass().new InnerClass();
        b.innerStaticMember = 3;
        System.out.println(c.innerStaticMember); // should be 3
        System.out.println(a.innerStaticMember); // 0 or 3?
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *