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:
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.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.