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
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on What does the static keyword do in C?

Parent

What does the static keyword do in C?

+12
−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?

History
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

Post
+11
−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 contexts 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]

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

1 comment thread

parameter array declarations of size 0 (5 comments)
parameter array declarations of size 0
alx‭ wrote 10 months ago · edited 10 months ago

It might be worth noting that static requires that the array size is at least 1. So, it's not valid C to have int x[static 0] as a parameter, which can be otherwise useful as an end-of-array marker.

For example, one may design a function which takes an array and a pointer to one-after-the-last-element to signal the end of it, instead of a size:

int *copy(int dst[], int end[0], src[])
{
    return mempcpy(dst, src, end - dst);
}

In the function above, it wouldn't be valid to use [static 0] for the parameter declaration. That's IMO a nonsensical restriction from ISO C.

Lundin‭ wrote 10 months ago

alx‭ static in the array parameter context refers to what the parameter points at, so it actually does make sense since we can't have zero-sized arrays in C. int end[0] is equally invalid, that's a gcc extension. And the only reason why gcc ever supported zero-sized arrays is historical - they used them as a work around for the "struct hack" back in the days before C got flexible array members. Also this is consistent with the rest of C - the compiler evaluates if the array is valid before it "gets adjusted" to a pointer to the first element. Why we can't write int arr[][] either.

alx‭ wrote 10 months ago

You're right. I wish zero-sized arrays get allowed some day in array parameters. :)

Lundin‭ wrote 10 months ago

alx‭ Something evil like this should be possible: static const size_t zero = 0;... void func (int x[zero]). Or maybe even void func (int x[ (int){0} ]). I have no idea why the mainstream compilers allow that, but it seems like they do.

alx‭ wrote 9 months ago

You could maybe even constexpr that :) I guess accepting such evilness is due to being more complex to reject, rather than an intention of defining the behavior. Nevertheless, I do think that param[0] has a valid use case in pointers to one-past-the-end of an array, which are useful for bounds.