Monday, October 10, 2011

Tentative Definitions in C

Suppose you want to access a variable from several translation units. You would declare it in a header file:

int foo;

You would optionally define it on a translation unit:

int foo = 2;

And you would refer to it on some other(s) translation unit(s):

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

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