4

In C, both variables and functions have external linkage at file scope by default. Why is the keyword extern only required for variables but not functions that are defined elsewhere? Note that there are two aspects to this question. Given that declarations at file scope default to external linkage:

  • Why historically is there no distinction for functions in whether their declaration has extern or not? Or, why objectively is no such distinction needed?
  • Why historically is there a distinction for variables in whether their declaration has extern or not? Or, why objectively is such a distinction needed?

For a minimal example, let's use the following two source files (tu stands for "translation unit").

tu1.c:

extern int i = 123;

tu2.c:

#include <stdio.h>

extern int i;

int main(void) {
  //extern int i;
  ++i;
  printf("%d\n", i);
  return 0;
}

We can compile them with GCC as follows:

gcc -c tu1.c
gcc -c tu2.c
gcc -o myprogram tu1.o tu2.o

(GCC issues the warning 'i' initialized and declared 'extern' for the first command, because it erroneously believes that extern "should be reserved for non-defining declarations". We can safely ignore it.)

Let's compare how the compiler behaves for slightly different versions of the source code:

  • extern int i; at file scope in tu2.c (no change to the above code):
    The output of myprogram is 124, as expected.
  • extern int i; in main (instead of at file scope) in tu2.c:
    This works equally well (but this style is not recommended: "A function should never need to declare a variable using extern.").
  • not declaring i anywhere in tu2.c:
    This doesn't work; for line i++; we get the following error: 'i' undeclared (first use in this function).
  • int i; without extern at file scope in tu2.c:
    This fails with an error: multiple definition of `i'.

I am wondering about the rationale for the last case: If a bare (extern-less) int i; defaults to external linkage, why do we need to supply the keyword extern explicitly? The answer seems to be in the standard (C99: 6.9.2 External object definitions), according to which int i; is a tentative definition, instantiated to an actual definition upon compilation. The logic would be that supplying the extern keyword instructs the compiler to treat the resulting construct as a declaration-which-is-not-a-definition. But if this is so: Why doesn't the same logic hold for function prototypes, for which it is well-known that extern is implicit?

I have a feeling that the right answer is related or close to this answer to "What is the rationale behind tentative definitions in C?", but I would like to know what would specifically go wrong if variables and functions were treated the same in the above regard.


There is a similar question about C++ and a relevant article by Peter Goldsborough.

Note for people used to programming in C++:

  • In C, variables at file scope (incl const ones) and functions default to external linkage.
  • In C++, there is the subtle difference that const global variables default to internal linkage.
Lover of Structure
  • 1,561
  • 3
  • 11
  • 27
  • 3
    I think of it basically as a convenience, to save some typing. There's no ambiguity: the `;` after the last `)` of a function declaration tells you that you're looking at a declaration, not a definition. So `extern` can be assumed. (There's an old "common model" for variables under which `extern` is basically optional there, too, but it's fallen out of favor.) – Steve Summit Mar 10 '23 at 12:27
  • 2
    functions are extern by default and it is specified by the C standard. – 0___________ Mar 10 '23 at 12:42
  • 2
    1. Function prototype does not define it. There is nothing like this with variables. `int x;` will always define the variable `x`. `int foo(void)` does not define function `foo` – 0___________ Mar 10 '23 at 12:45
  • @SteveSummit That is an interesting point. But can't we similarly check for variables whether there is an `=` after the declaration? Either way, in this case, I was assuming that the issue has to do with telling the compiler that the symbol has been or will be defined elsewhere (that is, this is about memory allocation, which differentiates definitions from declarations). I'm probably missing something. In any event, why can't we assume `extern` by default for variables (at file scope) as well? Would this inconvenience compilers? Or, if this is about backwards-compatibility, how exactly? – Lover of Structure Mar 10 '23 at 12:46
  • @0___________ Yes, but this is a language design question. I am aware of the standard, but why is it the way it is? – Lover of Structure Mar 10 '23 at 12:48
  • 2
    @LoverofStructure simply function definition is different than a declaration because it contains a function *body*. Variable declaration/definition is not and you need to use additional keyword to indicate what you are doing - declaring or defining – 0___________ Mar 10 '23 at 12:50

3 Answers3

5

Because the form of a function declaration indicates whether it is a definition or not.

A function declaration without a body is not a definition:

void foo(void);

A function declaration with a body is a definition:

void foo(void) { }

With int x;, there is ambiguity. Early C implementations treated this in different ways. We can mark it as definitely a definition by giving an initializer:

int x = 0;

However, with just int x;, some C implementations treated this as a definition (and may have allowed multiple such definitions to be coalesced into one, so this declaration appearing in a header file included in multiple translation units would result in just one definition in the linked program).

To remove the ambiguity, using extern without an initializer makes it a declaration that is not a definition.

If we were designing C from scratch, we could make a rule that int x; outside of any function is a declaration that is not a definition and that int x = 0; is a definition. So the current state of the language is not a logical necessity; it is a consequence of the history of how the language developed.

(However, such a rule would contrast with how we use declarations inside functions. Inside a function, we are accustomed to int x; being a definition. If we adopted the above rule for declarations outside functions, we would have to either live with the contrast between declarations inside functions with those outside functions or were would have to adopt the same rule for declarations inside functions.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    *`If we were designing C from scratch, we could make a rule that int x; outside of any function is a declaration that is not a definition and that int x = 0; is a definition`* IMO it would be very hard to read – 0___________ Mar 10 '23 at 12:52
  • If I were designing C from scratch, I would make either an `int x;` or `int x = 0;` outside of any function a syntax error. :-D – DevSolar Mar 10 '23 at 13:57
  • After all, nobody will be designing C from scratch anymore: it's been done decades ago. What we're doing now is fixing what it can be fixed without breaking (too much) the compatibility. There have been plenty of *C inspired* languages, designed from scratch to overcome C issues and limits, and more or less, maybe all of them added some other kind of issues. I think it's a quite normal risk. – LuC Mar 10 '23 at 14:31
  • Oh, and by the way, with C11/C17 is legal `extern int i3 = 3;` (I took the example right from the standard, clause 6.9.2) - just to throw in some more confusion... – LuC Mar 10 '23 at 14:36
  • Testing something like `extern int i3 = 3;` has then different results, upon the compiler used: Clang issues a warning (?) while GCC 12.2.0 is silent ... – LuC Mar 10 '23 at 14:56
  • 1
    @Luc: The C standard does not require a diagnostic for `extern int i3 = 3;`. Clang chooses to issue it, which the standard allows. – Eric Postpischil Mar 10 '23 at 17:36
  • @EricPostpischil yes, and I agree more with Clang warning than with the example accepted by the standard, in this very case! – LuC Mar 11 '23 at 16:03
1

It is to make a clear difference between declaration and definition.

Function prototype is clearly a declaration, not definition

int x; is definition so something else is needed to show the compiler that we do not define object x only declare it. extern int x; does it

0___________
  • 60,014
  • 4
  • 34
  • 74
1

A function declaration (without a body) is obviously just a declaration. That's why the storage class specifier extern is implicit.

A variable declaration (withoutextern), on the other hand, is also a definition. Adding extern to the variable declaration T x; means "somewhere there should be a definition of a variable x of type T".

If you practice modular programming there is no use for extern variables declarations since all variables declared outside of a function should have static storage class and be accessed through one of the functions from the module's API. In some situations though you may need the extra efficiency of accessing or assigning to a variable directly but then the extern declaration should be placed in the module's header file.

August Karlstrom
  • 10,773
  • 7
  • 38
  • 60