Complete Guide to Understanding static in C

Discussion in 'C' started by Trinity, Dec 2, 2012.

  1. Trinity

    Trinity New Member

    Joined:
    Nov 23, 2011
    Messages:
    28
    Likes Received:
    11
    Trophy Points:
    3
    Occupation:
    Software Engineer
    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++] = '.';
        }
    
        /*Null terminating the string*/
        myStr[i] = 0;
        printf(" The output string is %s\n", myStr);
    
        return 0;
    }
    
    char getNextChar()
    {
        static int index = 0;
        char retCh;
    
        retCh = STRING[index];
        index++;
    
        return retCh;
    }
     
    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.

    Lets compile it and run it.
    Code:
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc audio.c -Wall -o audio
    rupali@home-OptiPlex-745:~/programs/cstatic$ ./audio
    Size of Audio is 33
    
    All works fine and as expected.

    However, now we wish to add another feature to get or set metadata of video as well. So, we write another source code to support video as”

    File : video.c
    Code:
    #include <stdio.h>
    
    unsigned int size = 100;
    unsigned int duration;
    
    unsigned int getDurationVideo ()
    {
        return duration;
    }
    
    unsigned int getSizeVideo()
    {
        return size;
    }
    void setDurationVideo (unsigned int val)
    {
        duration = val;
    }
    
    void setSizeVideo(unsigned int val)
    {
        size = val;
    }
    
    This again stores the size and duration in global variables and manage them through functions. Lets compile both the sources to form object files.

    Code:
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc -c audio.c -Wall -o audio.o
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc -c video.c -Wall -o video.o
    
    Now link, both together to build an executable ‘mdataEngine’
    Code:
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc video.o audio.o -o mDataEngine
    audio.o:(.data+0x0): multiple definition of `size'
    video.o:(.data+0x0): first defined here
    collect2: ld returned 1 exit status
    
    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
    Code:
    #include <stdio.h>
    
    static 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;
    }
    
    File : video.c
    Code:
    #include <stdio.h>
    
    static unsigned int size = 100;
    static unsigned int duration;
    
    unsigned int getDurationVideo ()
    {
        return duration;
    }
    
    unsigned int getSizeVideo()
    {
        return size;
    }
    void setDurationVideo (unsigned int val)
    {
        duration = val;
    }
    
    void setSizeVideo(unsigned int val)
    {
        size = val;
    }
    
    Now, let’s compile and link:
    Code:
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc -c audio.c -Wall -o audio.o
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc -c video.c -Wall -o video.o
    rupali@home-OptiPlex-745:~/programs/cstatic$ gcc video.o audio.o -o mDataEngine
    
    The error is gone!

    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.)
    Code:
     
    rupali@home-OptiPlex-745:~/programs/cstatic$ objdump -t video.o
    
    video.o:     file format elf32-i386
    
    SYMBOL TABLE:
    00000000 l    df *ABS*	00000000 video.c
    00000000 l    d  .text	00000000 .text
    00000000 l    d  .data	00000000 .data
    00000000 l    d  .bss	00000000 .bss
    00000000 l     O .data	00000004 size
    00000000 l     O .bss	00000004 duration
    00000000 l    d  .note.GNU-stack	00000000 .note.GNU-stack
    00000000 l    d  .comment	00000000 .comment
    00000000 g     F .text	0000000a getDurationVideo
    0000000a g     F .text	0000000a getSizeVideo
    00000014 g     F .text	0000000d setDurationVideo
    00000021 g     F .text	0000000d setSizeVideo
    
    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
    Code:
    #include <stdio.h>
    #include "sfunc.h"
    
    void printN ()
    {
        printf("In printN\n");
        _printS();
    }
    
    static void _printS ()
    {
        printf("In static _printS\n");
    }
     
    The main source file
    File : main.c
    Code:
    #include <stdio.h>
    #include "sfunc.h"
    
    int main()
    {
        printf("Main started\n");
        printN();
        _printS();
    
        return 0;
    }
     
    Let’s compile the sources
    Code:
    rupali@home-OptiPlex-745:~/programs/cstatic/$ gcc main.c sfunc.c -Wall -o main
    sfunc.h:3:13: warning: \u2018_printS\u2019 used but never defined
    /tmp/cc8Wb2xL.o: In function `main':
    main.c:(.text+0x1b): undefined reference to `_printS'
    collect2: ld returned 1 exit status
     
    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.
     
  2. Rajesh M. Kanojia

    Rajesh M. Kanojia New Member

    Joined:
    Dec 9, 2012
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    INDIA
    really helpfull article.
    thank's
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice