cextern

the extern keyword in c language


I know some basic usage of extern, but one thing really bothers me.
The following picture is what I found in the specification.

img1 img2

If it is really as shown in the picture, why are the following two pieces of code different?

#include <stdio.h>

int i;

int main() {
    printf("i = %d\n", i);
    return 0;
}
#include <stdio.h>

extern int i;

int main() {
    printf("i = %d\n", i);
    return 0;
}

Additionally, the following code will have a warning:

warning: ‘i’ initialized and declared ‘extern’

WHY?

extern int i = 0;

Can someone tell me what the hell is going on with extern?


Solution

  • The text in the image is wrong because “extern” and “auto” are not default specifiers applied to declarations the way it says. The effects of extern depend on where declarations appear and what prior declarations there are. A declaration int i; at file scope cannot be said to have a default extern specifier because extern int i; has a different effect, discussed below.

    At file scope, int i; is a tentative definition for an object named i (C 2018 6.9.2 2). In spite of its name, a tentative definition is not a definition (just as a prospective employee on a job interview is not an employee). If no regular definition is present in a translation unit, a tentative definition will cause a definition to be created. However, because different C implementations treated int i; differently, the C standard does not define what happens if there are multiple definitions (C 2018 6.9 5, C 2018 4 2). Some C implementations coalesce multiple definitions resulting from compatible tentative definitions into a single definition. Some treat them as an error. The default changed in GCC version 10.

    At file scope, extern int i; is a declaration for an object named i that is not a definition. It does not reserve storage for i, and, if i is used in the program, there must be a definition for it somewhere else in the program. This declaration gives i external linkage unless a prior declaration is visible (see below).

    At file scope, extern int i = 0; is a definition for an object named i. According to the rules of the C standard, a declaration with an initialization is a definition, even if it has extern, so a compiler should treat it as a definition. However, it is a convention of users of C not to use extern with definitions. So declaring int i with extern and also providing an initialization with = 0 goes against that convention. But that is just a convention of users, not a rule of the C standard. So compilers will warn about this violation of convention, but a compiler that conforms to the C standard must accept this code as a definition of i. (If you turn warnings into errors, as with the -Werror switch with Clang or GCC, the compiler does not accept this code and becomes a non-conforming compiler.)

    As an example of another complication, if extern int i; appears where a prior declaration of i as static int i; is visible, the linkage of i at extern int i; is internal, not external (C 2018 6.2.2 4). The rules for extern cannot be stated as “It makes the linkage external” or even “At file scope, it makes the linkage external.” Its effects depend both on its scope and on prior declarations, using multiple rules stated in different places in the C standard.