Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rationale of static declaration followed by non-static declaration allowed but not vice versa

This code will compile and is well defined under current C standards:

static int foo(int);
extern int foo(int);

The standard specifies that in this situation (C11: 6.2.2 Linkages of identifiers (p4)):

For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,31) if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. [...]

... which means that the int foo(int) function is declared static int foo(int).

Swapping these declarations around like this:

extern int foo(int);
static int foo(int);

...gives me a compiler error using GNU GCC:

static declaration of 'foo' follows non-static declaration

My question is: What is the design rationale behind the second case being an error and not handled in a similar way as the first case? I suspect it has something to do with the fact that separate translation units are easier to manage and #include? I feel as though without understanding this, I can open myself up to some mistakes in future C projects.

like image 284
Dean P Avatar asked Apr 25 '17 15:04

Dean P


2 Answers

I think the idea of this confusing specification is that an extern declaration can be used inside a function to refer to a global function or object, e.g to disambiguate it from another identifier with the same name

static double a; // a declaration and definition

void func(void) {
  unsigned a;
  .....
  if (something) {
     extern double a; // refers to the file scope object

  }
}

Whereas if you use static you declare something new:

extern double a;  // just a declaration, not a definition
                  // may reside elsewhere
void func(void) {
  unsigned a;
  .....
  if (something) {
     static double a; // declares and defines a new object

  }
}
like image 118
Jens Gustedt Avatar answered Nov 15 '22 09:11

Jens Gustedt


I can imagine that the scenario leading to this asymmetry is that the default linkage of identifiers at global scope is extern.1 Failing to mark the definition of a previously static-declared function static as well would otherwise be an error because it is by default also an extern declaration.

Here is an illustration. In modern C, functions must be declared before use, but sometimes the implementation is at the end because it is secondary to the main purpose of the code in the file:

static void helper_func();       // typically not in a header

// code using helper_func()

// And eventually its definition, which by default 
// declares an **external** function. Adding 
// an explicit `extern` would not change a thing; it's redundant.
void helper_func() { /* ... */ }

This looks innocent enough, and the intent is clear. When C was standardized there was probably code around looking like this. That's why it is allowed.

Now consider the opposite:

extern void func(); // this could be in a header

// ... intervening code, perhaps a different file ...

static void func() { /* ... */ }

// code which uses func()

It's pretty clear that this should not be allowed. Defining a function static is a clear, explicit statement. A prior, contradicting extern declaration does not make sense.2 There is a good chance that this is an inadvertent name collision, for example with a function declared in a header. Probably not much code out there was looking like this at the time of formalization. That's why it is forbidden.

1 From the C17 draft, 6.2.2/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."

2 One could argue that an explicit extern declaration using the keyword, followed by a static definition, should be forbidden while an implicit one, without the keyword, could still be allowed (and that, correspondingly, a later explicitly extern function definition after a static declaration should be forbidden, while implicit ones are still allowed). But there is a limit to the hair-splitting a standard should do (and it's gone pretty far already).

like image 41
Peter - Reinstate Monica Avatar answered Nov 15 '22 10:11

Peter - Reinstate Monica