Why do we need the 'extern' keyword in C if file scope declarations have external linkage by default?

The extern keyword is used primarily for variable declarations. When you forward-declare a function, the keyword is optional.

The keyword lets the compiler distinguish a forward declaration of a global variable from a definition of a variable:

extern double xyz; // Declares xyz without defining it

If you keep this declaration by itself and then use xyz in your code, you would trigger an "undefined symbol" error during the linking phase.

double xyz; // Declares and defines xyz

If you keep this declaration in a header file and use it from several C/C++ files, you would trigger a "multiple definitions" error during the linking phase.

The solution is to use extern in the header, and not use extern in exactly one C or C++ file.


As an illustration, compile the following program: (using cc -c program.c , or the equivalent)

extern char bogus[0x12345678] ;

Now remove the "extern" keyword, and compile again:

char bogus[0x12345678] ="1";

Run objdump (or the equivalent) on the two objects.

You will find that without the extern keyword space is actually allocated.

  • With the extern keyword the whole "bogus" thing is only a reference. You are saying to the compiler: "there must be a char bogus[xxx] somewhere, fix it up!"
  • Without the extern keyword you say: "I need space for a variable char bogus[xxx], give me that space!"

The confusing thing is that the actual allocation of memory for an object is postponed until link time: the compiler just adds a record to the object, informing the linker that an object should (or should not) be allocated. In all cases, the compiler at leasts will add the name (and size) of the object, so the linker/loader can fix it up.


C99 standard

I am going to repeat what others said but by quoting and interpreting the C99 N1256 draft.

First I confirm your assertion that external linkage is the default for file scope 6.2.2/5 "Linkages of identifiers":

If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

The confusion point is that extern does not only alter the linkage, but also weather an object declaration is a definition or not. This matters because 6.9/5 "External definitions" says there can only be one external definition:

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

where "external definition" is defined by the grammar snippet:

translation-unit:
  external-declaration

so it means a "file scope" top-level declaration.

Then 6.9.2/2 "External object definitions" says (object means "data of a variable"):

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.

So:

extern int i;

is not a definition, because it does have a storage-class specifier: extern.

However:

int i;

does not have a storage-class specifier, so it is a tentative definition. And if there are no more external declarations for i, then we can add the initializer equal 0 = 0 implicitly:

int i = 0;

So if we had multiple int i; in different files, the linker should in theory blow up with multiple definitions.

GCC 4.8 does not comply however, and as an extension allows multiple int i; across different files as mentioned at: https://stackoverflow.com/a/3692486/895245 .

This is implemented in ELF with a common symbol, and this extension is so common that it is mentioned in the standard at J.5.11/5 Common extensions > Multiple external definitions:

There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

Another place where extern has an effect is in block-scope declarations, see: Can local and register variables be declared extern?

If there is an initializer for the object declaration, extern has no effect:

extern int i = 0;

equals

int i = 0;

Both are definitions.

For functions, extern seems to have no effect: Effects of the extern keyword on C functions as there is no analogous concept of tentative definition.

Tags:

C