С interview questions. Static.
So one of the most common questions at firmware interviews is what does the keyword static mean and how is it used?
The keyword static is used for two things: extend and limit. Extend the lifetime of a local variable and limit the scope of the variable and function.
Lifetime extension
Local (auto) variables, i.e. variables that you declare inside the function body and do not add the magic word static to the declaration, are stored either in the processor registers or on the stack when calling the function. And since there are usually not so many registers and stacks, and everyone needs these things, then since the function completes, your local variables is stop be valid. They are not physically destroyed, but you can`t use them because they will be overwritten by some other function. I.e. in fact, your local variables "live" only while the processor executes commands inside your function. But what should you do if you need to save the values of some variables between calls to your function? Well, you can declare these variables outside the function body, or you can add the keyword static to the declaration of such variables.
If adding static when declaring a local variable, it will no longer be stored on the stack and, accordingly, "destroyed" after the function is completed. This variable will be stored in RAM, in the .data or .bss section. I.e. it will behave almost exactly the same as if you defined this variable outside the function.
Look at the function:
Compile the code of this function and use the nm utility to look at the information about the symbols of the object file:
As you can see, the object file contains information only about the static variables static_init_local_var and static_local_var and they are assigned different classes: d and b respectively.
Classes d and D contain initialized variables, we get d if they are static and D otherwise.
For uninitialized global variables, we get b if they are static and B otherwise.
There is no information about local variables in the object file because these variables are allocated and initialized automatically on the stack each time when function is entered by a special assembler code generated by the compiler:
Scope limitation
The next thing that the keyword static is used for is to limit the scope of a variable or function. I.e. by adding static to declare a variable or function, we make it so that we can access this variable and function only from the code that is in the same file (translation unit). The mechanism is the same for both the variable and the function, but we will now consider how exactly it works.
And this mechanism works due to the presence of connection between declaration and definition of a variable or function.
Definition binds the name to the implementation, which can be either code or data: the definition of a variable prompts the compiler to reserve some memory area, possibly setting it some specific value; the definition of a function forces the compiler to generate code for this function
Declaration is a promise that the definition exists somewhere else in the program. Wherever the code refers to a variable or function, the compiler allows this only if it has seen the declaration of this variable or function before.
In the previous section, when looking at the object file, it was seen that the compiler assigns different classes to symbols. Then we worked with only two classes: d and b. But there are more of them:
领英推荐
The declaration and definition have been sorted out now let's deal with binding. Binding is the relationship of the name of an object (an object here means a variable or function) with a specific implementation of this object. The work on such binding is carried out by a linker and there are two types of binding: internal or external.
With internal binding, the symbol is visible to the linker only inside this translation unit. Visibility means that the linker will be able to use this symbol only when processing the translation unit in which the symbol was declared, and not later (as in the case of symbols with an external link). If an object or function has an external binding, then the linker will be able to see it when processing other translation units. Using the keyword static gives the symbol an internal binding. The keyword extern gives an external binding.
Thus, as mentioned above, by adding static when defining a variable and a function, we make it so that the linker sees this variable and function only when the translation unit in which the variable and function are defined is processed. And you will not be able to access (by normal means) a variable or a function declared as static from other translation units. Even if you add a static variable or function in to the header file and include this file in several .c files, each translation unit that includes this file will receive a unique copy of this symbol. This means that the compiler will literally allocate a completely new, unique copy for each translation unit, which, obviously, can lead to high memory consumption.
Let's show this with an example
header.h:
file1.h:
file2.h:
file1.c:
file2.c:
main.c:
Each translation unit including a header.h gets a unique copy of the variable, due to the fact that it has an internal connection.
Let's make sure of this by looking into the .map file obtained after compilation and see the presence of three variable variables located at three different addresses: