Suppose you want to access a variable from several translation units. You would declare it in a header file:
example.h
int foo;
You would optionally define it on a translation unit:
example.c
int foo = 2;
And you would refer to it on some other(s) translation unit(s):
example-main.c
#include <stdio.h> #include "example.h" int main() { printf("foo = %d\n", foo); return 0; }
If we compile and run the above we get:
$ gcc -Wall -o example example-main.c example.c
$ ./example
foo = 2
int foo;
from header.h
is a tentative definition. If you comment out the definition in example.c
, the tentative definition acts as a definition with an initializer of 0:
$ gcc -Wall -o example example-main.c example.c
$ ./example
foo = 0
But it can be considered to be better style to restrict header files to contain declarations, and not definitions, not even tentative ones. In that case, we would add a extern
storage specifier, which would turn our declaration on the header file from a tentative definition to a mere declaration:
example.h
extern int foo;
In this case, if we don't define foo
in any translation unit, the linker will error out:
$ gcc -Wall -o example example-main.c example.c
/tmp/ccS3FMxx.o: In function `main':
example-main.c:(.text+0x1d): undefined reference to `foo'
collect2: ld returned 1 exit status
All of the above is for C. C++ doesn't have tentative declarations, and therefore has some different rules on what constitutes a definition. In C++, int foo;
constitutes a definition, while extern int foo;
constitutes a declaration. So, if we leave out the extern
storage specifier in C++, the linker will error out:
$ g++ -Wall -o example example-main.cpp example.cpp
/tmp/cc8fzx4x.o:(.data+0x0): multiple definition of `foo'
/tmp/ccxLfXPo.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
No comments:
Post a Comment