extern int var;
I understand that when we use extern keyword with a variable as shown below, memory for that variable is not allocated. (It is just a declaration)
extern int i = 0;
And I know that if we declare an extern variable and also provide an initializer along with that declaration, then the memory is allocated for that variable.
Also the below program is printing 0
#include <stdio.h>
int i; // Can I treat this as declaration/definition?
int main()
{
printf("%d ", i);
return 0;
}
I feel, here the variable i is being assigned the value 0.
If (int i; as shown above) is definition, why below code is not giving multiple definition ERROR?
#include <stdio.h>
int i;
int i;
int i;
int main()
{
printf("%d ", i);
return 0;
}
Without an explicit initialization, all the int is in global space are called tentative definition. However, this is not allowed in local scope.
To quote the C11 standard, chapter §6.9.2, External object definitions
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.
Related
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.
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.
The following snippet works fine
extern int i;
int i;
int main(){
return 0;
}
Here what I got is, 'i' is declared and then defined. Since there is only one definition so thats perfectly fine.
int main(){
extern int i;
int i;
return 0;
}
Now, the above one gives the following error
new.cpp: In function ‘int main()’:
new.cpp:5:6: error: redeclaration of ‘int i’
int i;
^
new.cpp:4:13: note: previous declaration ‘int i’
extern int i;
Whats the problem here? Here also there is single definition of 'i'.
To understand the difference, you need to get familiar with a concept called tentative definition in C. To quote the C standard:
C11, draft, §6.9.2, External object definitions
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.
What you have in the first snippet is only a tentative definition for i. You can have as many tentative definitions for an object as you want (but only one definition is allowed):
int i; // tentative definition
int i; // tentative definition
int i; // tentative definition
int main(void) {
return 0;
}
is valid.
Here, i has external linkage and tentatively defined. If i is defined in somewhere in the same translation unit, then that'll be the actual definition of i. If there's no other definition of i is found in the translation unit, then this becomes the full definition as if it was defined like:
int i = 0;
int main(void) {
return 0;
}
But the second snippet int i; is not a tentative definition. Only objects with external linkage can be defined tentatively. In second snippet, The declaration extern int i; says i is defined elsewhere with external linkage. But the next line int i; says i is defined with no linkage (local automatic variables do not have any linkage -- this is not a tentative definition). So there's a conflict of definition for i. Hence, the first one snippet is fine but second isn't.
In the second case, there are two declarations of i in one scope. One says "there is a variable i defined outside this function"; the other says "there is a variable i defined inside this function". Without a new scope, that isn't allowed.
The rules are different inside and outside functions.
Note that you could use:
#include <stdio.h>
int i = 21;
int main(void)
{
extern int i;
i = 37;
{
int i = 57;
printf("%d\n", i);
}
printf("%d\n", i);
return 0;
}
This compiles OK (unless you include -Wshadow in your compilation options when using GCC or Clang), and produces 57 and 37 on the output (as pointed out by CoffeeAndCode in a comment).
See also How do I use extern to share variables between source files?
Compiling code 1 gives an error 'i redefined', but code 2 shows no similar error. Why is it so?
Code 1
static int i; //Declaring the variable i.
static int i=25; //Initializing the variable.
static int i; //Again declaring the variable i.
int main(){
return 0;
}
Code 2
int i; //Declaring the variable i.
int i=25; //Initializing the variable.
int i; //Again declaring the variable i.
int main(){
return 0;
}
Both should compile.
Both int i; and static int i; are tentative definitions in C as they do not have an initializer and are not extern. You are allowed multiple tentative declarations and at most one non-tentative definition for any object in a translation unit so long as the definitions don't conflict in type or linkage.
ISO/IEC 9899:1999 6.9.2:
A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with a 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 definitions 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.
I have the below C code and I am expecting it to throw an error like "multiple declaration of variable", but it is not doing so.
#include <stdio.h>
int i;
int i;
int main()
{
printf("%d",i);
return 0;
}
Now the output is 0, but why?
And one more thing below code gives error what is expected
#include <stdio.h>
int main()
{
int i;
int i;
printf("%d",i);
return 0;
}
O/p is error saying re declaration of i
The first definition of i is a tentative definition (the 2nd is also a tentative definition). They are "de facto" definitions though (and definitions serve also as declarations), no mistake about it.
Quote from the Standard:
6.9.2/2
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.