Home > other >  At what point during compilation or linking of C code are extern variables implicitly defined?
At what point during compilation or linking of C code are extern variables implicitly defined?

Time:10-30

If I have a project with the following 3 files in the same directory:

mylib.h:

int some_global;
void set_some_global(int value);

mylib.c:

#include "mylib.h"
void set_some_global(int value)
{
    some_global = value;
}

main.c:

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

int main()
{
    set_some_global(42);
    printf("Some global: %d\n", some_global);
    return 0;
}

and I compile with

gcc main.c mylib.c -o prog -Wall -Wpedantic

I get no errors or warnings, and the prog program prints 42 to the console.

When I first tried this, I expected there to be a "multiple definition" error or some kind of warning since some_global is not declared extern in the header file. Upon researching this issue, I discovered that in C the extern is implicit on variable declarations outside of functions (and also that the opposite is true for C , which can be demonstrated by using g instead of gcc in the compilation line above). Also, if I change the line in mylib.h from a declaration to a definition (e.g. int some_global = 1;), I do get the "multiple definition" error that I expected (this is nothing shocking).

My main question is: where is the variable being defined? It appears to be implicitly defined somewhere, but at what point does either the compiler or linker realize it needs that variable defined and does so?

Also, why is it that if I explicitly declare the variable as extern in the mylib.h file, I get "undefined reference" errors unless I explicitly declare the variable in one and only one *.c? I would expect that given the reason why the code above works (that extern is implicit), that explicitly declaring extern wouldn't make a difference. Why is there a difference in behavior?


Follow up

After the answer below corrected me that the code in mylib.h is a "tentative definition" rather than a declaration, I discovered this related answer with more details on such matters:

https://stackoverflow.com/a/3095957/7007605

CodePudding user response:

  1. Your code compiles and links without error only because you use gcc which was compiled with -fcommon command line option "The -fcommon places uninitialized global variables in a common block. This allows the linker to resolve all tentative definitions of the same variable in different compilation units to the same object, or to a non-tentative definition. (...) It is mainly useful to enable legacy code to link without errors." This was default prior to version 10, but even now many toolchains are still build with this option enabled.

  2. Never define data in the header files. Place only extern definitions of the variables in the header files.

It should be:

extern int some_global;
void set_some_global(int value);

mylib.c:

#include "mylib.h"

int some_global;

void set_some_global(int value)
{
    some_global = value;
}

main.c:

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

int main()
{
    set_some_global(42);
    printf("Some global: %d\n", some_global);
    return 0;
}

CodePudding user response:

int some_global; is a tentative definition. In GCC before version 10, GCC produced an object file treating this as a common symbol. (This behavior is still selectable by a switch, -fcommon.) The linker coalesces multiple definitions of a common symbol to a single definition.

  • Related