5

I am confused with the usage of extern in the same file as shown in the code below. The first case was actually a solution to print a global variable in C (when same name local variable exist), but I am not able to understand how that worked and how the third case didn't work.

Case 1:

int a = 10;
int main()
{
    int a = 20;
    {
        extern int a; // Is this telling the linker to use global variable? 
        printf("A value: %d\n", a);
    }
    return 0;
}

Case 2:

extern int a; // If in the above case extern was telling linker to use global variable 
              // then how in this local variable is getting referred
int main()
{
    int a = 20;
    {
        printf("A value: %d\n", a);
    }
    return 0;
}

Case 3:

// int a = 10;
int main()
{
    int a = 20;
    {
         extern int a; // Why now I get a linking error
         printf("A value: %d\n", a);
    }
    return 0;
}
Jonathan Leffler
  • 698,132
  • 130
  • 858
  • 1,229
Pranjal
  • 55
  • 3
  • Possible duplicate: https://stackoverflow.com/questions/14335742/can-local-and-register-variables-be-declared-extern – Jose Jun 05 '18 at 11:51

4 Answers4

6

In the first case you have a global a that you override with a local (automatic) a that you again override with the global a (extern can only refer to variables global in some module). It will print 10.

In the second case you have a global a, that resides in this or in another module (c file/compilation unit) that you override with a local a. It will print 20.

In the third case you have a local a that you override with a global a that apparently does not exist in any of your compilation units, hence the linker error.

Paul Ogilvie
  • 24,620
  • 4
  • 19
  • 40
  • 1
    The global `a` does not have to reside in another translation unit. A single `extern int a;` at file scope is sufficient to define the object per the tentative definition rule of **6.9.2 External object definitions**, paragraph 2. – Andrew Henle Jun 05 '18 at 12:01
  • 1
    @AndrewHenle `C99` draft says, that "A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition." Nothing is said about `extern` specifier, so `extern int a;` is not a tentative definition. I couldn't figure out whether standard considers it undefined behaviour, but in case of `gcc` refering to such variable (without any definition at any translation unit) yields a linker error. – Przemek Jun 05 '18 at 12:31
  • @Przemek If "Nothing is said about `extern` specifier", how can you conclude "`extern int a;` is not a tentative definition."? – Andrew Henle Jun 05 '18 at 12:47
  • Since `extern` is a storage class specifier, `extern int a;` at file scope is not a tentative definition. It is an unused and undefined declaration. – Jonathan Leffler Jun 05 '18 at 12:58
  • @AndrewHenle Because there is no sentence implying that. Furthermore, behaviour of `extern int a;` is similar in block and in global scope - if there is no definition, it fails to link. This behaviour is exactly opposite to a tentative definition. – Przemek Jun 05 '18 at 12:59
  • @JonathanLeffler this is how it works in practice, but is it specified by the standard? – Przemek Jun 05 '18 at 13:07
  • 1
    @Przemek: [C11 §6.9.2 ¶2 External object definitions](https://port70.net/~nsz/c/c11/n1570.html#6.9.2p2): _A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition._ When the compiler sees `extern int a;` at file scope, it is a declaration of an identifier without an initializer but **with** a storage class specifier (`extern`), so it is **not** a 'tentative definition'. To be a tentative definition, it would have to be `int a;` or `static int a;`. – Jonathan Leffler Jun 05 '18 at 13:30
  • @AndrewHenle: Your first comment in this sequence contradicts what the standard says. The `extern int a;` at file scope is categorically not a 'tentative definition'. – Jonathan Leffler Jun 05 '18 at 13:41
4

(Note that the edits to the code in the question seem to make parts of this answer no longer quite correct.)

Per 6.2.2 Linkages of identifiers, paragraph 4 of the C standard:

For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, 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.

So, in your first two cases, the inner extern int a; has a prior declaration - a global int a; in your first case or extern int a; in your second case - so the extern int a; declaration refers to the global.

For the third case, the remainder of paragraph 4 is relevant:

If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

Also, paragraph 6 states:

The following identifiers have no linkage: an identifier declared to be anything other than an object or a function; an identifier declared to be a function parameter; a block scope identifier for an object declared without the storage-class specifier extern.

So the declaration in your third case is referring to an extern int a;

However, there is no actual int a; defined anywhere. And because the extern int a; in your third example appears in a block scope, and there is no actual definition of the int a; object elsewhere, your program fails to link.

Per 6.9.2 External object definitions, paragraph 2 states:

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

So the block-scope extern int a; declaration of your third case does not qualify as a tentative definition.

Andrew Henle
  • 1
  • 3
  • 24
  • 53
1

Case 1:

The extern int a ; declaration declares a int a variable that is defined elsewhere and thus shadowing the a variable declared here : int a = 20 ;. The linker is then looking for a global variable a and finds it in the same file because of the int a = 10 ; declaration. The output is 10.

Case 2:

Here the extern int a ; declaration has no effect. Inside main the local variable declared here int a = 20 ; is used and hence the output is 20.

Case 3:

This is similar to case 1. It does compile correctly, but is does not link because extern int a ; declares a int a variable that is presumably defined elsewhere, which is not the case because you commented out the int a = 10 ; declaration.

Jonathan Leffler
  • 698,132
  • 130
  • 858
  • 1,229
Jabberwocky
  • 45,262
  • 17
  • 54
  • 100
1

Case 1:- In first code block extern int a; indicates that variable a is declared here but tells linker to find the definition of a above main() not in the main(). If linker is able to find the definition of a above main() then it will link otherwise results in linker error. In your case linker will take a as 10.

Case 2 :- In this case above globally declared extern int a; is telling to linker that definition of a may be in other file or in the same file in the main() function. Here extern int a; is saying that if you need it, there will be a definition of a with static duration and external linkage (a global variable) defined either in this file or in another file. That declaration is hidden by the definition inside main(), so the printf() uses the local variable. This

printf("A value: %d\n",a);

consider locally declared a. So it prints 20.

Case 3 :- In this case the statement

extern int a; // Why now I get a linking error

causing linker error because linker will try to find definition of a above main() and it's not there(you commented) so it's results in linker error.

Achal
  • 11,664
  • 2
  • 16
  • 35
  • 1
    In case 2, the `extern int a;` is saying that if you need it, there will be a definition of `a` with static duration and external linkage (a 'global variable') defined either in this file or in another file. That declaration is hidden by the definition inside `main()`, so the `printf()` uses the local variable — and it is immaterial whether the global variable exists since it is not used. – Jonathan Leffler Jun 05 '18 at 13:54
  • Thanks @JonathanLeffler for pointing that. It's very useful information for me. – Achal Jun 05 '18 at 14:02