Search code examples
cglobal-variablesheader-files

Global variables initialization in C header file


I was wondering why declaring a global variable in a header file which is included in multiple source in C was working

#ifndef INC_MAIN_H_
#define INC_MAIN_H_

int my_var;

#endif 

But assigning it a default value does not work:

#ifndef INC_MAIN_H_
#define INC_MAIN_H_

int my_var = 0;

#endif 

It shows multiple compiling errors in each files where I include this main.h

multiple definition of `my_var'; app/src/testfile.o:app/inc/main.h:4: first defined here

I know declaring global variables this way is not the best practice, but I can't figure out why adding an assignation would cause compiling errors and I can't find clear answers about it.


Solution

  • With the build tools and switches you are using, int my_var; acts mostly like a declaration that is not a definition. Multiple declarations of an object identifier are allowed.

    int my_var = 0; is a definition. There should be only one definition.

    Due to the history of C use and development, int my_var; is technically a tentative definition. If there is no regular definition of my_var in the translation unit (the source file being compiled, including all the files it includes), it acts as a regular definition.

    However, then when you have this in a header file that is included in multiple source files, you have multiple definitions. When there are multiple definitions of the same object identifier with external linkage, the C standard does not define the behavior.

    When there are multiple regular definitions, your build tools report an error. However, they treat definitions that came from tentative definitions differently: They allow them and coalesce them into a single definition. Again, this is due to the history of how C developed and was used. Also, this behavior changed in GCC recently. Prior to GCC version 10, tentative definitions were coalesced by default, as described above. With GCC 10 and later, tentative definitions are not coalesced, and multiple definitions will result in an error. The old behavior can be requested with the switch -fcommon.

    To avoid tentative definitions, you should declare the identifier in the header as extern int my_var;, which is just a declaration and not a tentative definition, and you should have exactly one source file containing int my_var = 0;, which is a regular definition and not a tentative definition.

    Additional information is here, here, and here.