Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of tentative definitions in C [duplicate]

Tags:

c

c99

Let's say I have two source files: main.c and a.c:

main.c:

#include <stdio.h>

int a;

int i;
int i;

int main(void)
{
    printf("a = %d\n", a);
    printf("i = %d\n", i);

    return 0;
}

a.c:

int a;

Then, according to latest C99 draft 6.9.2 External object definitions p. 2 (emphasis mine):

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

Compilation (no warnings given):

gcc -g -std=c99 -pedantic-errors -Wall -Wextra main.c a.c

I understand that for i variable there two are tentative definitions and since main.c does not have "true" external definition they are merged into such one. What about a ? Do I state correctly, that tentative definitions are not "shared" between multiple source files (i.e. translation units) ?

like image 921
Grzegorz Szpetkowski Avatar asked Jul 16 '14 13:07

Grzegorz Szpetkowski


1 Answers

Your program is erroneous: it defines the same external name more than once. The GNU tool chain follows a relaxed linkage model which does not flag this as an error; it merges the multiple definitions. However, that is effectively a language extension. Strictly conforming ISO C programs cannot define a name more than once.

The notion of a "tentative definition" is purely syntactic, within one translation unit. At the end of a translation unit, any definitions which are still tentative are "cemented" as definitions.

The reason int i; is called "tentative" is that it is "weak" in a sense. It can be overridden by a later definition. If, by the end of the translation unit, it isn't then it turns into int i = 0.

So for instance this is valid:

int i;  /* might become int i = 0 */

int i = 42; /* i is now defined; the tentative definition is replaced */

In this situation, i is understood to be defined once, not twice. The translated unit contains a single definition of i.

like image 142
Kaz Avatar answered Nov 13 '22 23:11

Kaz