I'm reading the C Standard N1570 and faced some misunderstanding about linkage. As specified in 6.2.2. Linkages of objects
:
5 If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.
So I guessed that there is no difference between extern
and no storage-class specifier in the declaration of identifiers of objects with file scope.
Let's condider the following example:
test.h
:
#ifndef _TEST_H
#define _TEST_H
int a;
void increment();
#endif //_TEST_H
test.c
:
#include "test.h"
void increment(){
a += 2;
}
main.c
:
#include <stdio.h>
#include "test.h"
int main(int argc, char const *argv[])
{
increment();
printf("a = %d\n", a);
}
Since a
is declared to have external linkage (file scope, no storage class specifier) a = 2
is printed as expected.
So I replaced the declaration of a
to have extern
specifier and expected no difference (according to the 6.2.2#5
I cited above):
test.h
:
#ifndef _TEST_H
#define _TEST_H
extern int a; // <---- Note extern here
void increment();
#endif //_TEST_H
But now the linker complains:
CMakeFiles/bin.dir/main.c.o: In function `main':
main.c:37: undefined reference to `a'
liblibtest.a(test.c.o): In function `increment':
test.c:4: undefined reference to `a'
test.c:4: undefined reference to `a'
collect2: error: ld returned 1 exit status
How does the Standard explain this behavior? Since identifiers have the same linkage in both cases I expected the linker behavior to be the same too.
In the fist case int a
, is a tentative definition.
In second case, a definition for a
is missing, only declaration is there. That's why linker complains.
Quoting C11
, chapter §6.9.2
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.