c - static and extern one again - c

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

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.

Related

Usage of extern in the same file

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;
}
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.
(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.
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.
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.

Order of `static` definition and `extern declaration` in a translation unit

I am unable to understand why this doesn't work.
extern int i;
int main()
{
printf(" %d ", i);
}
static int i =3;
Also, this doesn't work:
extern int i;
static int i =3;
int main()
{
printf(" %d ", i);
}
But if static variable is defined before the extern declaration it works:
static int i =3;
extern int i;
int main()
{
printf(" %d ", i);
}
As I understand from extern int itells that i is present somewhere else and here how it looks lik(int i)
But, somewhere else means:
1) Maybe, later point in the same translation unit as a global variable.
2) Maybe, in some other translational unit.
I was thinking that (1) would be valid even though static int i = 3 has restricted i's scope to the current translation unit where it is defined.
Isn't static int i =3 global( i mean atleast it is visible in the translation unit) here even though it has the restricted scope to its translation unit? Then why isn't compiler unable to find it?
When I compile the first two versions I get the following compile time error:
error: static declaration of ‘i’ follows non-static declaration
note: previous declaration of ‘i’ was here
I am unable to understand this error message. Also, why it is complaining it as a static declaration isn't it a definition also?
C11 6.2.2 Linkages of identifiers Section 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.
So the second declaration will follow the first, back to your examples, the 1st and 2nd example i will have an extern storage-class. The compiler thinks that's an error.
While in the 3rd example, i will be static because static shows first. That should be no problem.
And, in the Section 7 of C11 6.2.2 Linkages of identifiers
If, within a translation unit, the same identifier appears with both internal and external
linkage, the behavior is undefined.
So it's better not to declare the same variable with both static and extern in the same translation unit.
Well, a variable is either extern or static. Remember that static at a global level restricts its visibility only to the current translation unit, whereas extern dictates that it is visible across different translation units.
It makes no sense to declared something as static and again as extern. Doing so is undefined behaviour, so don't do it.

Why does a global static variable take precedence over an extern within a function?

this is hard to explain in text, so I will give an example.
//f1.c
int a = 5;
int main()
{
printf("func2() output is: %i\n", func2() );
return 0;
}
//f2.c
static int a = 3
int func2()
{
extern int a;
return a;
}
When I compile and run this, I get 3, while I was expecting 5. Can anyone explain to me why I get 3? I would have thought that by using extern within the function, it wouldn't use the value of the static variable.
From n1256 §6.2.2 ¶4:
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. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.
So, extern within function scope means that the variable has external linkage by default, but if there's another visible definition, that definition is used instead.
The static class-modifier denotes that the variable will only be visible in this translation-unit (f2.c) By using extern for a, you only declared that variable, but not defined it. Yet, there was an a available in the translation-unit f2.c, so this one will be used.

Can local and register variables be declared extern?

I have been wondering whether an extern can be declared locally and a register variable. If it can be what would be the restrictions imposed?
Local variables can be declared extern in some cases
Let's read the C99 N1256 standard draft.
The standard calls "local variables" as having "block scope".
6.7.1/5 "Storage-class specifiers" says:
The declaration of an identifier for a function that has block scope shall have no explicit storage-class specifier other than extern.
Then for what it means to add extern to a local variable, 6.2.2/4 "Linkages of identifiers" says:
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. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.
Lets break down those cases.
no prior declaration
void f() {
extern int i;
}
is the same as:
extern int i;
void f() {}
except that the declaration is only visible inside f.
This is because i has no prior declaration visible. So i has external linkage (the same linkage as global variables).
prior declaration specifies no linkage
int i;
void f() {
extern int i;
}
is the same as:
void f() {
extern int i;
}
because the prior declaration int i specifies no linkage because paragraph 6 says:
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.
prior declaration specifies internal or external linkage
extern int i;
void f() {
extern int i;
}
is the same as:
extern int i;
void f() {}
and:
static int i;
void f() {
extern int i;
}
is the same as:
static int i;
void f() {}
because in both cases we have a previous visible external and internal (static) linkage declarations respectively.
Initialize local extern
Invalid C:
void f() {
extern int i = 0;
}
because the block scope declaration has an initialization.
Valid C:
extern int i = 0;
void f() {}
but arguably bad style because equivalent to the shorter:
int i = 0;
void f() {}
because 6.7.8 Initialization says:
If the declaration of an identifier has block scope, and the identifier has external or internal linkage, the declaration shall have no initializer for the identifier.
Can local variables be declared extern?
No. But a global variable can be declared extern locally.
// file1.c
int Count;
// file2.c
void foo(void) {
extern int Count;
Count++;
}
Can register variables be declared extern?
No. A variable may not be extern and register.
C11 dr 6.7.1 Storage-class specifiers
1 storage-class-specifier:
typedef
extern
static
_Thread_local
auto
register
Constraints
2 At most, one storage-class specifier may be given in the declaration specifiers in a
declaration, except that _Thread_local may appear with static or extern)
6.9 External definitions of C99 states:
The storage-class specifiers auto and register shall not appear in the declaration
specifiers in an external declaration.
You only are allowed to define a global variable as extern. Telling the compiler (and linker) that it is defined elsewhere.
A local variable only exist in the local scope, as it is created on the stack or in a register. When the execution is not in the scope (anymore) the stack is unrolled (so free space becomes available again) or the register is used for other things, and the variable does not exist (anymore).
So defining a local extern would be 'weird' and impossible (due to the stack usage).
The phrase register variable is not clearly to me, so I would take one bold guess on what OP is really curious about, and rephrase the original question as: Could local variables be declared with extern specifier?, illustrated by the following snippet:
int main() {
extern int x; // Is this OK?
return 0;
}
The answer is yes.
scope (visibility) and storage are two independent and connected concept. Here, x is one local variable (scope), and it's only visible within this block. extern dictates the storage, meaning this is merely one declaration, this variable is defined somewhere else. Would recommend the C standard for definite reference.
As for the omitted register part, I assume OP meant one variable with register storage-class-specifier, like register int x. Then, it's illegal to specify register and extern at the same time.
int main() {
extern auto int x; // This is wrong.
return 0;
}
At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.
The symmetric question would be: is it valid to specify auto or register with global or external variables, and this is exactly what Alexey Frunze's answer is about.
auto int x; // This is wrong.
int main() {
return 0;
}

External variable declaration and definition

a)The definition of an external variable is same as that of a local variable,ie, int i=2; (only outside all functions).
But why is extern int i=2; too working as the definition? Isn't extern used only in variable declaration in other files?
b)file 1
#include<stdio.h>
int i=3;
int main()
{
printf("%d",i);
fn();
}
file2
int i; // although the declaration should be: extern int i; So, why is this working?
void fn()
{
printf("%d",i);
}
OUTPUT: 3 in both cases
For historical reasons, the rules for determination of linkage and when a declaration provides a definition are a bit of a mess.
For your particular example, at file scope
extern int i = 2;
and
int i = 2;
are equivalent external definitions, ie extern is optional if you provide an initializer.
However, if you do not provide an initializer, extern is not optional:
int i;
is a tentative definition with external linkage, which becomes an external definition equivalent to
int i = 0;
if the translation unit doesn't contain another definition with explicit initializer.
This is different from
extern int i;
which is never a definition. If there already is another declaration of the same identifier visible, then the variable will get its linkage from that; if this is the first declaration, the variable will have external linkage.
This means that in your second example, both file1 and file2 provide an external definition of i, which is undefined behaviour, and the linker is free to choose the definition it likes best (it may also try to make demons fly out of your nose). There's a common extension to C (see C99 Annex J.5.11 and this question) which makes this particular case well-defined.
In C, an extern with an initialization results in a variable being allocated. That is the declaration will be considered a defining declaration. This is in contrast with the more common use of extern.
The C standard says:
6.9.2 External object definitions
.....
If the declaration of an identifier for an object has file scope and an initializer, the
declaration is an external definition for the identifier.
As for the second part of your question, your declaration int i at file scope has external linkage. If you want to give it internal linkage you need to declare it static int i. The C standard says:
6.2.2 Linkages of identifiers
......
If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

Resources