Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Notifications
Mark all as read
Q&A

What does the static keyword do in C?

+10
−0

What exactly does the static keyword do in C? I have seen it used in several diverse contexts:

1) As a variable outside a function:

#include <stdio.h>

static int x = 5;
int main (void)
{
  printf("%d\n", x);
}

2) As a variable inside a function:

void func (void)
{
  static int count=0;
  printf("%d\n", count++);
}

3) As part of a function declaration/definition:

static void func (void);

4) As part of a function parameter list containing an array:

void func (int arr[static 5]);

All of these uses seem quite unrelated. What exactly is the purpose of static in all the above examples?

Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+9
−0

Storage class specifiers
static is strictly speaking a storage class specifier, which is an attribute given to a variable or a function stating how it is allocated, how it is accessible and how long it will be valid. Other examples of storage class specifiers are extern and register.

It should be noted that a storage class specifier should always be written to the far left in a declaration, doing otherwise is obsolete syntax. From C17 6.11.5: "The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature."

The static keyword is however also used in a few other context too, which will be addressed below.


Scope and linkage
If a static variable or function is declared at file scope - outside any function body - it ensures that the variable or function is only accessible from that .c file and any header included by that file: from within the same translation unit where it was declared.

This is called internal linkage. A variable or function with internal linkage may not be referred to by other files and cannot be accessed as a "global" variable with extern. It is a way to give the variable private encapsulation, to reduce access and scope. It's generally considered good practice.

In example 1), x is only accessible by the .c file where it was declared and not by other .c files. This reduces namespace conflicts and clutter, but more importantly also prevents other parts of the program to use the variable by accident or intentionally.

Similarly in example 3), func cannot be accessed by other .c files. A function declared as static should be kept local to the .c file and not exposed through a header file.


Storage duration
Storage duration specifies the lifetime of a variable. If declared as static, it has static storage duration. This means that the variable is valid and persists throughout the whole execution of a program.

Variables with static storage duration have special rules of initialization. They will get initialized before main() is called and the program is started and not when the code line containing the declaration is encountered. Meaning that in example 1) the code x = 5 is executed before main() is called. And in example 2) the code count=0 is also executed before main() is called. This means that the initialization code is only executed once and not each time a function is entered, as would be the case with local variables.

Since the variables are initialized before main() is called, it also mean that the initializer list must only contain compile-time constants, since it is always resolved at compile-time. So we can't initialize a static variable to the result of another variable or a function call etc.

Furthermore, all variables with static storage duration are guaranteed to be initialized. These are the only variables in C with such a guarantee. If the programmer does not write initialization code explicitly, then they will get initialized to zero implicitly. (static pointers will get initialized as null pointers.) So in example 2) the code count=0 is strictly speaking superfluous - the variable would have been set to zero anyway. Though it is good practice not to rely on implicit zero but to write the initializer explicitly.


Memory allocation
Variables with static storage duration also get allocated at special places in memory and not on the stack as local variables. The C standard doesn't mandate where they are allocated, but in practice all C implementations create two segments for them, normally referred to as .data and .bss ("block starting symbol", a confusing name with lots of history behind it). This is for example standardized by the ELF file format. All static storage variables that were initialized explicitly to a non-zero value get stored in .data. All other static storage variables that were initialized to zero, explicitly or implicitly, get stored in .bss.

Before main() is called, the "C run-time" start-up code executes initialization of .data and also sets everything in .bss to zero. This is the reason why they are kept as two different segments: setting a whole chunk of variables to zero in one go is faster.


Static inside function parameter array declarations
Disclaimer: this part of the answer is an advanced topic and only included here for completeness. The vast majority of C programmers never use these features.

With the C99 version of the C standard, various special features for declaring arrays as parameters to a function were introduced. One such feature enables us to specify that an array parameter should at least have a certain size and that it cannot be a null pointer.

In example 4) void func (int arr[static 5]); means that arr must at least have size 5 and it cannot be a null pointer.

Historically, compilers haven't really supported this this feature until quite recently. Some modern compilers have implemented diagnostics for it. Here is an example:

void func (int arr[static 5])
{}

int main(void)
{
  int arr[4]={1,2,3,4}; // too small array
  func(arr);
  func(NULL); // null pointer
  return 0;
}

For the first error, gcc 11.2 gives:

warning: 'func' accessing 20 bytes in a region of size 16 [-Wstringop-overflow=]

clang 13.0.0 gives:

warning: array argument is too small; contains 4 elements, callee requires at least 5 [-Warray-bounds]

For the null pointer error, we have to do gcc -Wall in order to get:

warning: argument 1 to 'int[static 20]' is null where non-null expected [-Wnonnull]

clang 13.0.0 always gives a warning:

warning: null passed to a callee that requires a non-null argument [-Wnonnull]

Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »