18

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.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
user6898756
  • 183
  • 6
  • So, you've read paragraph 2, good, now try reading paragraph 6, then paragraph 4, even after that, if you have question, please come back. :) – Sourav Ghosh Sep 29 '16 at 10:12
  • 2
    With gcc you get this error, but not with clang. – Jabberwocky Sep 29 '16 at 10:14
  • 1
    (Fun fact: In C++ mentions this code (as valid!) in the example in [basic.link]/6.) – Kerrek SB Sep 29 '16 at 10:15
  • I've read all the paragraphs and not once. a_Local has no linkage, right. – user6898756 Sep 29 '16 at 10:19
  • @KerrekSB Could you fix your link? Cause now I wonder why C++ acts differently. – user6898756 Sep 29 '16 at 10:48
  • The link is a section reference. It's as intended. You can look it up in one of the recent working drafts, e.g. in [N4604](http://wg21.link/n4604). It's quite possible that C++ has a different rule than C, so I can't comment on this question, but I did notice that GCC and Clang also disagree in C++. – Kerrek SB Sep 29 '16 at 10:54
  • I had a Deja-vu - like this question was written by me long time ago with the same or similar wording. Strange. – AnArrayOfFunctions Sep 30 '16 at 16:41

3 Answers3

16

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
P.P
  • 117,907
  • 20
  • 175
  • 238
  • It seems I did not think about this exception. Any rationale for this statement? – user6898756 Sep 29 '16 at 10:31
  • 2
    @user6898756 Rationale: Forbidding unintelligible results of bizarre and esoteric rules with no discernible benefit? Just maybe? Plus, perhaps, a better chance to build a correct compiler and linker. – Peter - Reinstate Monica Sep 29 '16 at 17:31
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;
}
tversteeg
  • 4,717
  • 10
  • 42
  • 77
  • Using your explanation, a_Local should not work either. – user6898756 Sep 29 '16 at 10:23
  • FYI: If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope ) will end strictly before the scope of the other entity (the outer scope ). Within the inner scope, the identifier designates the entity declared in the inner scope; **the entity declared in the outer scope is hidden (and not visible) within the inner scope**. – user6898756 Sep 29 '16 at 10:26
  • @user6898756 that's not how it works , see the quote in the question: all declarations of identifier with linkage denote the same entity (they do not hide any other declaration with linkage) – M.M Sep 29 '16 at 11:28
  • @M.M I was referring to _inside the a_External scope, a_Internal is still accessible_. No. It's not. It's hidden by having an a_External declaration. The only way to access an a_Internal is per the same linkage rule, which I quoted. – user6898756 Sep 29 '16 at 11:46
  • @M.M The quoted text decrees that "each declaration of a particular identifier with **external** linkage denotes **the same object**", and it also decrees that "each declaration of an identifier with **internal** linkage denotes **the same object**". Like with all natural language the semantics are up for grabs, but one possible interpretation is that we have two distinct and unrelated sets of "same" objects, those with internal linkage; and those with external linkage. ... – Peter - Reinstate Monica Sep 29 '16 at 17:39
  • ... Your interpretation "all declarations of identifier with [any] linkage denote the same entity", by contrast, establishes one overall bag of "same" across linkages. That actually doesn't make much sense because declaring the same identifier with different linkage is, apparently, forbidden. – Peter - Reinstate Monica Sep 29 '16 at 17:39
  • @PeterA.Schneider it's clearly intended that all denote the same entity. That's exactly why there can be the rule about redeclaring an identifier with different linkage causing UB. (If there were these two pools it would not be possible to redeclare with different linkage in the same pool) – M.M Sep 29 '16 at 21:57
  • @M.M Again we have a possible semantics issue. You say "it would not be possible to redeclare with different linkage in the same pool"; I say well, it *is indeed not* possible. It is "possible" in the sense that you can produce a sequence of characters which appear to do that; it is "not possible" in the sense that this sequence of characters is not a valid C program. – Peter - Reinstate Monica Sep 30 '16 at 06:49
  • @PeterA.Schneider now we argue whether a program with undefined behaviour (no diagnostic required) is a valid C program... – M.M Sep 30 '16 at 07:11
  • @M.M. Are we? ;-). (I somehow hope not...) – Peter - Reinstate Monica Sep 30 '16 at 08:46
1

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.

msc
  • 33,420
  • 29
  • 119
  • 214