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

◇◆丶佛笑我妖孽 提交于 2020-07-15 11:50:12

问题


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.


回答1:


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

  }
}



回答2:


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).



来源:https://stackoverflow.com/questions/43614828/rationale-of-static-declaration-followed-by-non-static-declaration-allowed-but-n

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!