0

I'm checking various situations that could happen in declarations with different linkages, and I have 2 questions.

I. I have the following code:

#include <stdio.h>  

static int a = 19;
         
   int main(void)
   {
      extern int a;
      {
          extern int a;
          printf("braces - %d\n", a);
      }   
      printf("main - %d\n", a);
  }

which compiles well with both clang and gcc, with the same result of 19 being printed in both pritfs. As i may understand, all as are static as per 6.2.2., 4) of the Standard. The only thing that I don't understand in it is that why file-scope a is visible for a in main and braces? Should't the file-scope one be hidden as per footnote 31? When I define the other a in other file with different value, both printfs` output is still 19.

II. Now I do the following:

#include <stdio.h>  

static int a = 19;
         
   int main(void)
   {
      int a;                           //change in this line
      {
          extern int a;
          printf("braces - %d\n", a);
      }   
      printf("main - %d\n", a);
  }

gcc yells variable previously declared ‘static’ redeclared ‘extern’, while clang acts normal and prints a = 0 for a in main (yes, it is garbage value) and still 19 in braces.
I guess gcc applies here 6.2.2. 7) of the Standard, while clang doesn't. Which one's interpretation is correct and what is going on in here?

I can only assume that gcc ''matches'' a in braces with a in main (which has no linkage) and makes it's linkage external, and then sees that it conflicts with file-scope static a. And, once again, why not making braces a refer to a in the other file (footnote 31?)?


My current understanding is, in fact, close to the one in the accepted answer in here, even though i do understand that C++ has differences from C (and i ask about C).

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Anton Tretyakov
  • 303
  • 1
  • 9

1 Answers1

0

The only thing that I don't understand in it is that why file-scope a is visible for a in main and braces?

The variable a declared in the file scope is visible in the block scope of main

   static int a = 19;
         
   int main(void)
   {
      extern int a;
      //...

until the declaration in main redeclares the variable in the file scope. So the variable in main denotes the same variable declared in the file scope.

In the block scope of the compound statement

int main(void)
{
   extern int a;
   {
       extern int a;
       //...

the declaration of the variable a in the enclosing scope of main is visible until the declaration of the variable a in the block scope of the compound statement. So the variable declared in the compound statement denotes the same variable that is declared in the enclosing block scope of the function main. All three variables denote the same variable with the internal linkage.

As for the second program then according to the C Standard (6.2.2 Linkages of identifiers)

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

the program has undefined behavior because the variable declared in the block scope of the function main

static int a = 19;
     
int main(void)
{
    int a; 
    // ...

hides the variable declared in the file scope with internal linkage. So according to the C Standard (6.2.2 Linkages of identifiers)

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

the variable a declared in the block scope of the compound statement has external linkage. As a result the same identifier in the translation unit has external and internal linkages.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you comrade for your answer! I guess 6.2.1. 4) slipped away from my understanding. Hiding only happens when identifiers designate different entities, but in the first case all three designate a single entity. – Anton Tretyakov Oct 21 '20 at 19:15
  • 1
    @AntonTretyakov You are right. In the second program the declaration of the variable a without storage specifier extern hides the declaration of a with linkage declared in the file scope. – Vlad from Moscow Oct 21 '20 at 19:17
  • The phrase "in a scope in which a prior declaration of that identifier is visible..." seems a bit clunky and ambiguous. In normal English usage, saying that an X is visible in Y typically means that there is an X, within Y, that is visible from some location (which, depending upon context, might or might not be within Y), as opposed to meaning that there is an X somewhere in the universe which is visible *from within* Y. I don't think it's clear that the Standard meant to say the new identifier is declared "when a prior declaration is in scope", in cases where the prior declaration is not... – supercat Oct 21 '20 at 20:42
  • @supercat Consider the following program int x; int main( void ) { /* here is the declaration of x is visible*/ { /* in this code block the declaration still visible*/ { /* and in this code block the declaration is visible*/ } } } Until the identifier will be redeclared without the storage specifier extern it will be visible in each nested declarative region. – Vlad from Moscow Oct 21 '20 at 20:48
  • ...contained within the same scope as the new one. I think the classification as UB of scenarios where a compilation unit contains internal and external definitions for the same identifier is intended to, among other things, allow implementations to treat an `extern` which is contained within a function, but shares the name of a file-scope `static` object, to be treated either as a reference to the file-scope object or an an imported reference separate from it. – supercat Oct 21 '20 at 20:48
  • @supercat Within a function a variable with the storage specifier is not a definition. It is a declaration. If the function definition precedes the declaration of the variable declared in the file scope and the variable has internal linkage then the program has undefined behavior. If the variable declared in a function with the storage specifier extern and there is precedent declaration of the file scope variable then the function variable has the same linkage as the file scope variable. – Vlad from Moscow Oct 21 '20 at 20:57
  • @VladfromMoscow: In English, describing something as visible "in something" typically describes the location of the object, some other prepositions or phrases like "from within something" would imply a description of potential observers' location, and some could be used to describe either, based on context. Implementations are certainly allowed to treat earlier declarations in outer scopes as described, but that doesn't imply that the authors of the Standard intended to forbid any other treatments. – supercat Oct 21 '20 at 21:01
  • @supercat I am sure that there is clear written in the Standard and in my answer that based on the Standard. – Vlad from Moscow Oct 21 '20 at 21:03
  • @supercat Reread the quote #4 provided in my answer and the text in bold. – Vlad from Moscow Oct 21 '20 at 21:05
  • The precise phraseology in the Standard is "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." Do you know anything else in the Standard that would disambiguate whether "in" describes the location of the thing that is visible [common English usage], or the location from which it it can be seen? – supercat Oct 21 '20 at 21:07
  • @supercat To resolve the point of view just consider the simple example when a variable declared in the file scope for example with the storage specifier static and a variable that is declared in main with the storage specifier extern. And output its value. It will be the same value that the variable in the file scope has. That is the variable in the file scope is visible in the block scope of main. You can also include the variable in main in nested block scopes and you will get the same result. – Vlad from Moscow Oct 21 '20 at 21:13
  • @supercat Here is a demonstrative program #include static int x = 10; int main(void) { printf( "%d\n", x ); { printf( "%d\n", x ); { printf( "%d\n", x ); } } return 0; } – Vlad from Moscow Oct 21 '20 at 21:16
  • @supercat But if you will run this program #include static int x = 10; int main(void) { printf( "%d\n", x ); { int x = 20; printf( "%d\n", x ); { extern int x; printf( "%d\n", x ); } } return 0; } you can get error prog.c:13:15: error: variable previously declared ‘static’ redeclared ‘extern’ extern int x;. Now is all clear? – Vlad from Moscow Oct 21 '20 at 21:19
  • For a compiler to interpret the preposition "in" as describing the location of the previous declaration would be less of a linguistic stretch than many of the other stretches the authors of clang and gcc use to justify some useless corner-case behaviors. Reliance upon future compilers to refrain from such interpretations would thus seem unwise, regardless of how present compilers seem to behave. – supercat Oct 21 '20 at 21:26