cmemorystaticembedded

static variable in (embedded) C, instancing of header-file and memory consumption?


I have a header file foo.h with a static int c[100];. If I include foo.h in main.c, it creates another instance of c[100]. If I do so again in bar.c, I get yet another instance:

foo.h:

#ifndef FOO_H
#define FOO_H

static int c[100];
void foo(void);

#endif

foo.c

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

void foo(void)
{
    c[0] = 100;
    printf("c: %d \taddress: %p \t size: %lu\n", c[0], (void *)&c, sizeof(c));
}

bar.h

#ifndef BAR_H
#define BAR_H

void bar(void);

#endif

bar.c

#include <stdio.h>
#include "bar.h"
#include "foo.h"

void bar(void)
{
    c[0] = 111;
    printf("c: %d \taddress: %p \t size: %lu\n", c[0], (void *)&c, sizeof(c));
}

main.c

#include <stdio.h>
#include <unistd.h>
#include "foo.h"
#include "bar.h"

int main()
{
    c[0] = 22;
    printf("c: %d \taddress: %p \t size: %lu\n", c[0], (void *)&c, sizeof(c));

    foo();

    bar();

    return 0;
}

The result shows me I have three blocks in memory at different adresses with 400Bytes each:

c: 22   address: 0x102de4000     size: 400
c: 100  address: 0x102de4190     size: 400
c: 111  address: 0x102de4320     size: 400

So it uses 1200 Bytes of memory, correct?

Besides the fact that this is just an example in which the array could be way bigger (and therefore I would use a pointer with runtime memory allocation):


Solution

  • So it uses 1200 Bytes of memory, correct?

    Yes, 3 * 100 * sizeof(int)

    • Is there any optimization happening during compile time?

    Per default, gcc does some optimizations, yes, but nothing that affects what you've observed afaik.

    • Is the compiler (I use gcc at the moment) smart enough to realize that only c[0] is used?

    It sees three different c[0] since each c is unique for each translation unit. It may be able to figure out that no more than a single element in each array is used if you don't programatically look for proof that you have arrays of 100 ints. The as-if rule lets it optimize away things that don't matter in order to make the program behave as you wrote it to behave.

    • Is the operating system (name one) smart enough not to waste the memory?

    It may overcommit dynamic memory allocations and not actually give you memory until you need it, but in this case, I don't think it has much choice but to give you the 3 static arrays that you requested.

    • When I code for embedded microcontrollers, is there any difference to running this program on another OS (memory-wise)?

    The size of int could be different, so you may see fewer or more bytes allocated. It will always be 3 * 100 * sizeof(int) though.


    If you only want one array accessible from all translation units, don't define it in the header file. extern declare it in the header and define it in foo.c. Also, making it static ensures that each translation unit gets a unique instance of the array, which I presume that you don't want.

    foo.h:

    #ifndef FOO_H
    #define FOO_H
    
    extern int c[100];  // note the extern declaration
    void foo(void);
    
    #endif
    

    foo.c:

    #include "foo.h"
    #include <stdio.h>
    
    int c[100];        // here's the definition
    
    void foo(void)
    {
        c[0] = 100;
        printf("c: %d \taddress: %p \t size: %zu\n", c[0], (void *)&c, sizeof(c));
    //                                        ^^
    //                        use the correct conversion specifier
    }
    

    This way, all translation units that include foo.h will use the c that is defined inside foo.c, so there will only be one array shared by them all.


    If you only want one array accessible only in foo.c, move the definition from foo.h to foo.c:

    foo.h

    #ifndef FOO_H
    #define FOO_H
    
    // no c defined here
    void foo(void);
    
    #endif
    

    foo.c

    #include "foo.h"
    #include <stdio.h>
    
    static int c[100]; // here's the only definition
    
    void foo(void)
    {
        c[0] = 100;
        printf("c: %d \taddress: %p \t size: %zu\n", c[0], (void *)&c, sizeof(c));
    }