External, internal and no linkage or why this does

2019-04-18 17:32发布

问题:

According to C standard:

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function. Within one translation unit, each declaration of an identifier with internal linkage denotes the same object or function. Each declaration of an identifier with no linkage denotes a unique entity.

In my example we have three separate declarations with each identifier having a different linkage.So why doesn't this work?

static int a; //a_Internal

int main(void) {
    int a; //a_Local
    {
        extern int a; //a_External
    }
    return 0;
}

Error:

In function 'main': Line 9: error: variable previously declared 'static' redeclared 'extern'

Why does compiler insist that I'm redeclaring instead of trying to access external object in another file?

Valid C++ example for reference:

static void f();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  int i;                        // #2 i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3 external linkage
  }
}

Both Clang and VC seem to be okay with my C example; only some versions of GCC (not all) produce the aforementioned error.

回答1:

§6.2.2, 7 says:

If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.

So, your program has undefined behaviour.

§6.2.2, 4 says that

extern int a; //a_External

has external linkage because the prior declaration visible in the scope int a; //a_Local has no linkage. But

static int a; //a_Internal

declares a with internal linkage. Hence, it's undefined per §6.2.2, 7.



回答2:

The compiler is giving this error because inside the a_External scope, a_Internal is still accessible, thus you are redeclaring a_Internal from static to extern in a_External because of the name collision of a. This problem can be solved by using different variable names, for example:

static int a1; //a_Internal

int main(void) {
    int a2; //a_Local
    {
        extern int a3; //a_External
    }
    return 0;
}


回答3:

C standard says:

In the set of translation units each declaration of a particular identifier with external linkage denotes the same entity (object or function). Within one translation unit, each declaration of an identifier with internal linkage denotes the same entity.

In the set of translation units we cannot have multiple distinct external entities with the same name, so the types of each declaration that denotes that single external entity should agree. We can check if types agree within one translation unit, this is done at compile-time. We cannot check if types agree between different translation units neither at compile-time nor at link-time.

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. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

static int a; //a_Internal

int main(void) {
    int a; //No linkage
    {
        extern int a; //a_External
    }
    return 0;
}

Here the previous declaration of identifier a has no linkage, so extern int a has external linkage. It means that we have to define int a in another translation unit. However GCC decided to reject this code with variable previously declared static redeclared 'extern' error, probably because we have undefined behavior according to C standard.