The keyword ‘static’ has been widely used in many programming languages. I know it is there in Java, C and C++. I am pretty sure, it is must be used in other languages as well, even though it might depict different attributes and characteristics. However, we are going to learn about the keyword ‘static’ in C, what does that mean, and where and why to use it.

The Keyword ‘static’ in C

In C, a variable or a function can be defined ‘static’. One can define a variable or a function static by just prefixing it with the keyword ‘static’. Here is a sample:

Code:

/*Static Variable*/
static int sVar;

/*Static Function Prototype*/
static int myFunc();

/*static Function Definition*/
static int myFunc()
{
…..
…
}

Therefore, to make a variable static, just prefix its declaration with the keyword ‘static’. And to make a function static, prefix its prototype and definition with the keyword ‘static’.

Making a variable static in C means to create just one instance of it of it and that too at the beginning of the program execution. Making a function static in C, means that the function can only be used within that file.

Static Variables

As stated, for C static variables, memory is allocated once the program begins, and de-allocated when the program exits. Therefore they have a lifetime for the entire run of the program. However, it has a limited scope. In case of global static variables, i.e. if declared at the top of a C file, its scope is limited to the file only. Hence, it would not be visible outside the file. In case of local static variable of a function, its scope is limited to that function and not visible to code outside the parenthesis of the function. Lets understand the scope and lifetime of a static variable with the help of some examples.

Understanding the lifetime of the static variable

In the following code, we want a function such that it returns the next character of a string every time it gets called. However, it can be easily done by passing the index of the string as a parameter, and the function doesn’t care about maintaining the index. What if, we don’t want to pass a parameter to the function as it adds up to the stack every time the function is called.
The solution is a static variable, whose extended life time helps in maintaining the index of the string. Here is an example code:

Code:

#include <stdio.h>
#define MAX 20
#define STRING "Go4Expert"

char getNextChar();

int main()
{
char ch;
int i = 0;
char myStr[MAX];

/*Run a loop for each character of the STRING*/
while ((ch = getNextChar()))
{
myStr[i++] = ch;
myStr[i++] = '.';
}

In the code, having a closer look at the definition of the function “getNextChar()”,

Code:

char getNextChar()
{
static int index = 0;
char retCh;

retCh = STRING[index];
index++;

return retCh;
}

It declares a static integer variable ‘index’, which is incremented once for each call to this function. Since, the variable ‘index’ is allocated memory in the process image when the executable begins running, it gets maintained there. Any modification to its value is retained even when the function returns. Therefore, once incremented from

Code:

index = 0

to

Code:

index = 1

, the value of ‘index’ is updated to ‘1’. When the next time a call is made to ‘getNextChar()’, the value of ‘index’ is still ‘1’ and get incremented to ‘2’ now. Hence, this is how, it always retrieves us the next index of the string, without us worrying about passing ‘index’ as a parameter or even adding an extra effort code to maintain it. It was simply done using a static variable because of the its lifetime till the complete run of the program.

To see the output:

Code:

The output string is G.o.4.E.x.p.e.r.t.

which is pretty expected after understanding the static variable lifetime.

Understanding the scope of the static variable

Moving on to understanding how scope of the static variable affects, lets create a full fledged application with multiple source files. With an intention to give a simple example, we are coding the most unreal and abstract kind of a metadata engine which will give audio metadata.

File : audio.c

Code:

#include <stdio.h>

unsigned int size = 12;

unsigned int getSizeAudio()
{
return size;
}

void setSizeAudio(unsigned int val)
{
size = val;
}

int main()
{
setSizeAudio(33);
printf("Size of Audio is %d\n", size);

return 0;
}

The source stores size of the audio file in a global variable which are managed through functions. The above program sets and receives the size metadata.

Ohhh, we got a linking error of multiple definition of size. If we check back, compiler points out that the variable ‘size’ has been defined twice, once in audio.c and other in video.c. Until, they were compiled in their separate compilation unit, it was fine. When linked together, symbol resolution got confused and that’s why points out that the global variable ‘size’ is being defined in both the source files.

However, it is our requirement to use ‘size’ in both the files as in audio.c it represents the audio size, whereas in video.c, it represents the video size.

The solution is using ‘static’ variable whose scope is limited within the file in which its defined. Hence, ‘size’ in audio.c would not be visible to video.c and vice versa.

Here is how the sources look like using static variables,
File : audio.c

Now we know in what kind of scenarios static variables are most suited and should be used.

In the ELF

All uninitialized static variables go to the .bss section of the ELF process image. And hence, all uninitialized static variables are by default initialized to zero.
Besides, all explicitly initialised variables are part of the .data section.

This can be easily verified through the objdump linux command. Using the already compiled ‘video.o of the above example,
(objdump with -t’ option displays the symbol table. Learn the basic usage of objdump through its man page.)

Note here the symbol ‘size’ is part of .data section as it has been initialised whereas symbol ‘duration’ is part of .bss section as it is uninitialized.

Static Functions

Static Functions as already mentioned are the ones which have limited scope within the file in which it has been defined. Its exactly the same scope concept as that of the static variable explained in the section above. To illustrate with a help of a simple example, Again, to explain scope of static function, we need to take multiple sources.

The header file with the prototypes.
File : sfunc.h

Code:

void printN ();
static void _printS();

The source file with the definitions of a non static function and a static function.
File : sfunc.c

Its again a error saying main.c source file could not find the definition of _printS() function. Note this is a static function defined in ‘sfunc.c’, which should explain the error. _printS() being a static function is only visible within ‘sfunc.c’ When the linker tried to resolve it in file ‘main.c’, it could not find any visible definition and hence complained about it through the linking error.

Now, to confirm, lets remove the call to _printS() from ‘main.c’ and see if that makes the error go.

File : main.c

Code:

#include <stdio.h>
#include "sfunc.h"

int main()
{
printf("Main started\n");
printN();

return 0;
}

Compiling again,

Code:

rupali@home-OptiPlex-745:~/programs/cstatic/$ gcc main.c sfunc.c -Wall -o main
rupali@home-OptiPlex-745:~/programs/cstatic/$ ./main
Main started
In printN
In static _printS

It works fine. Hence, we know that static functions should only be defined when our requirement includes it to be invisible and not accessible to other sources.