Search code examples
cexternallanguage-lawyerdeclarationlinkage

Mixing declarations with extern, static and no storage specifier in global scope


I have been investigating when it is possible to mix variables declared with extern, static and no storage specifier in global scope. The results have left me quite confused.

This is what I found (each paragraph is a separate compilation unit):

/* ok */
int x;
int x;

/* ok */
int f();
int f();

/* ok */
int x;
extern int x;

/* ok */
int f();
extern int f();

/* error: static declaration follows non-static declaration */
int x;
static int x;

/* ok (no warning) */
int f();
static int f();

/* ok */
extern int x;
int x;

/* ok */
extern int f();
int f();

/* ok */
extern int x;
extern int x;

/* ok */
extern int f();
extern int f();

/* error: static declaration follows non-static declaration */
extern int x;
static int x;

/* error: static declaration follows non-static declaration */
extern int f();
static int f();

/* error: non-static declaration follows static declaration */
static int x;
int x;

/* ok (no warning) */
static int f();
int f();

/* ok */
static int x;
extern int x;

/* ok */
static int f();
extern int f();

/* ok */
static int x;
static int x;

/* ok */
static int f();
static int f();

I get the same exact results with gcc and clang but I cannot find a pattern in what works and what doesn't work.

Is there any logic here?

What does the C standards say about mixing global declarations declared with extern, static and no storage specifier?


Solution

  • If you define an identifier without the keyword static, it's published in the object file and can be accessed by other modules. So if you again define that identifier without static in another module, you'll get a conflict: two published identifiers.

    If you use the keyword static, then (most of) the rest of this doesn't apply.

    The problem is the difference between declaring the identifier and defining it. The first says "there's going to be an identifier X with this type". The second says "here's something that I'm going to call X of this type".

    • With functions it's easy: don't provide the body, and it's merely a declaration. Provide the body, and it's a definition too. You can use extern to make this explicit in a header file but since it's the default, that's not usual.

    • With variables it's harder. Simply declaring the variable defines it too - thus you can initialise it while defining it. If you want to only declare it, you need to use the keyword extern - but then you can't initialise it too. You're saying "there's going to be a variable called X" - so you can't have the temerity of deciding its definition too!

    That's why in header files all variables should be explicitly declared either extern or static:

    • The first is usual: there will be a common variable that everyone can access. Don't forget that somewhere, in one module, you need to provide the actual definition, without the extern keyword, with an optional initialisation value.
    • The second is rare: each module that includes the header file will have its own, non-conflicting variable with that particular name. The compiler may be able to not allocate memory for it (especially if it's a constant) - but if it does allocate memory, it'll be different in every module. Why would you do this? Maybe the (forced) inline functions of that header file need each module to have their own copy...