Understanding `extern` storage class specifier in C - c

Consider given C codes :
#include<stdio.h>
extern int i;
int main(){
printf("%d", i);
return 0;
}
It gives Compilation error. While if I initialize extern int i=10; then output is 10.
#include<stdio.h>
extern int i=10; //initialization statement
int main(){
printf("%d", i);
return 0;
}
And also, if I assign int i=10;then output is 10.
#include<stdio.h>
extern int i;
int i=10; //assignment
int main(){
printf("%d", i);
return 0;
}
And
#include<stdio.h>
extern int i;
int main(){
int i=10; //assignment
printf("%d", i);
return 0;
}
Variation :
#include<stdio.h>
extern int i;
int i=20;
int main(){
int i=10;
printf("%d", i);
return 0;
}
Since, int i is local variable, so output is 10.
Please, can you explain some important point about extern storage class in C
I read somewhere, a declaration declares the name and type of variable or function. A definition causes storage to be allocated for the variable or the body of the function to be defined. The same variable or function may have many declaration, but there can be only one defition for that variable or function.

Consider
int i;
declared outside all the functions of the programme including main.
This means i will have
file scope
static storage duration
space is allocated for i. #This is important.
Consider
extern int i;
declared outside all the functions of the programme including main.
This means i will have
file scope
static storage duration
i is only declared, not defined which means space is not allocated.
i is assumed to be defined somewhere else, may be, in an include file
Consider
extern int i=10;
declared outside all the functions of the programme including main.
This is the case where you're initializing the extern variable i. In this case
Space is allocated for i.
i will be initialized to 10.
The keyword extern is neglected which means i is NOT declared only.
Note
For extern int i it is mandatory that the variable should be defined somewhere else, ie in another source file. If this is not the case, you will a compile-time error.

The extern storage class specifier is used to give a reference of a global variable that is visible to ALL the program files. When you use 'extern', the variable cannot be initialized however, it points the variable name at a storage location that has been previously defined.

when the code references a symbol via the extern modifier, it means the symbol is actually defined elsewhere rather than in the current file.
An exception, if the extern modififier is used for a symbol that is actually defined in the current file, that is OK.

The extern int i; declaration just tells the compiler that there somewhere (in this or another source) exists an actual declaration of i in global scope. It's not in itself the actual declaration of i. Therefore it's inappropriate to assign a value to i in that declaration (which is why the second try is invalid). Your first attempt is invalid since you don't have an actual declaration of i.
Your third is OK though as you have an actual declaration of i, but you wouldn't have to have the extern int i; declaration.
Your last samples involves i in both local and global scope. In such a case the variable in local scope will be used. That's why you don't get 20 printed.

Related

Extern declaration of variable with no link to a definition

#include <stdio.h>
extern int i;
int main()
{
int i = 5;
printf("%d", i);
return 0;
}
From what I understand, there is no global variable named i to which the external declaration of i should refer. So it should produce a linker error. Yet this code runs successfully and prints 5. Why so?
#include <stdio.h>
extern int i; // this is one variable called "i"
int main()
{
int i = 5; // it is second variable called "i"
printf("%d", i);
return 0;
}
You have variable i declared (but never defined) in the global (file) scope.
You also have variable i defined in the local scope of function main. The global scope i and the automatic function scope i variables are not related.
As you never use the global i it was optimized out by the compiler and the linker did not have to search for the object i in object and library files.
BTW your main function should be int main(void) not int main()
Because there is a local definition with init int i =5; within main().
The linker does not have a problem with i being accessed form inside a function which has a local definition for it.
The comments by Selvin, Refugnic Eternium and Barmar provide more insights.
The comment by M.M. helpfully quotes from standard. "Since you never use it..." might be confusing. You do use it, yes, but only in local scope with a definition, not outside that local scope. The experiment proposed by Baramar changes exactly this detail.

Why does `int x; extern int x;` not compile? [duplicate]

In the following program, I thought that extern int i; will change the following i to refer to the i defined outside main:
#include <stdio.h>
extern int i=1; // warning: 'i' initialized and declared 'extern'
int main()
{
int i=2;
printf("%d\n", i);
extern int i; // error: extern declaration of 'i' follows declaration with no linkage
printf("%d\n", i);
return 0;
}
What is the reason of the "error: extern declaration of 'i' follows declaration with no linkage", where "declaration with no linkage" refers to int i=2;?
After I remove int i=2 in main,
the error is gone,
the warning "warning: 'i' initialized and declared 'extern'" on extern int i=1; also disappear . Why is that?
Thank you for explanations!
#include <stdio.h>
int i=1; // external variable
int main()
{
int i=2; // local variable
printf("%d\n", i); // print local variable i==2
{
extern int i; // point to external variable
printf("%d\n", i); // print external variable i==1
}
return 0;
}
Once you define a variable named i inside your main function, the i at file scope is masked and cannot be accessed (unless you have its address).
When you later add the declaration extern int i, this conflicts with the local variable named i at the same scope since locals can't have external linkage. It does not give you access to the global i.
When you remove the local i, the extern int i declaration matches up with the definition at file scope, so there is no error. As for the warning on extern int i=1;, that did not go away for me on gcc 4.1.2, so that depends on the compiler.
Question: What is the reason of the "error: extern declaration of 'i' follows declaration with no linkage", where "declaration with no linkage" refers to int i=2;?
Answer: We do not need to use the extern keyword here on line 3 when it is a single file in a program and there is no other file in the same program or other location on the same file where the variable int i has its definition. There are two main reasons we can use extern keyword in C:
1. When we want to declare a variable explicitly/globally but without its definition.
2. To make the variable globally visible from any other file in a multi-file program or other location of the same file (see Ihdina's exmaple for this scenario).
Compiling your code on my system I get the following error,
error: extern declaration of 'i' follows non-extern declaration .
which totally makes sense that the compiler detects the extern on line 9 as a duplicate declaration of the same variable int i on line 7.
Question: After I remove int i=2 in main,
the error is gone,
the warning "warning: 'i' initialized and declared 'extern'" on extern int i=1; also disappear . Why is that?
Answer: After removing the int i=2; the error was gone on my system but still I have the following warning message:
warning: 'extern' variable has an initializer [-Wextern-initializer]
Basically my system (Apple LLVM version 8.1.0 (clang-802.0.42)) does not like the explicit initialization with extern keyword. So, you should modify your code as per Ihdina's answer which compiles without error.

How to understand C language redefinition and redeclaration error? [duplicate]

When I compile the code below
#include<stdio.h>
int main()
{
int a;
int a = 10;
printf("a is %d \n",a);
return 0;
}
I get an error:
test3.c: In function ‘main’:
test3.c:6:5: error: redeclaration of ‘a’ with no linkage
test3.c:5:5: note: previous declaration of ‘a’ was here
But if I make the variable global then it works fine.
#include<stdio.h>
int a;
int a = 10;
int main()
{
printf("a is %d \n",a);
return 0;
}
Why is declaring the same global variable twice not an error, but doing that for a local variable is an error?
In C, the statement int a; when made at file scope, is a declaration and a tentative definition. You can have as many tentative definitions as you want, as long as they all match each other.
If a definition (with an initializer) appears before the end of the translation unit, the variable will be initialized to that value. Having multiple initialization values is a compiler error.
If the end of the translation unit is reached, and no non-tentative definition was found, the variable will be zero initialized.
The above does not apply for local variables. Here a declaration also serves as a definition, and having more than one leads to an error.
The other reason I could think of is that un-initialized global variables are stored in the BSS (Block Structured Segment) where are the global variables that are initialized are stored in Data Segment.
I am guessing that there is some kind of a name space resolution and when there is a conflict the variable in the Data segment overrides the one in the Block Structured Segment.
if you were to declare
int a =5
int a = 10
in the global scope (both in the data segment) there would be conflict as expected.
You can't have two global variables with the same name in C program.
C might allow multiple definitions in the same file scope through the tentative definition rule, but in any case all definitions will refer to the same variable.
Local Variable
In C, multiple local variables are not "merged" into one.
All the local variables with the same name will be referring to the different int-sized piece of memory.
#include<stdio.h>
int main()
{
int a;
int a = 10;
printf("a is %d \n",a);
return 0;
}
So when assigning the memory to the redeclaration of the same variable it gives an Error.
Global Variable
In C, multiple global variables are "merged" into one. So you have indeed just one global variable, declared multiple times. This goes back to a time when extern wasn't needed (or possibly didn't exist - not quite sure) in C.
In other words, all global variables with the same name will be converted to be one variable - so your
#include<stdio.h>
int a;
int a = 10;
int main()
{
printf("a is %d \n",a);
return 0;
}
will be referring to the same int-sized piece of memory.

Why tentative definition concept in C not valid for local variables [duplicate]

When I compile the code below
#include<stdio.h>
int main()
{
int a;
int a = 10;
printf("a is %d \n",a);
return 0;
}
I get an error:
test3.c: In function ‘main’:
test3.c:6:5: error: redeclaration of ‘a’ with no linkage
test3.c:5:5: note: previous declaration of ‘a’ was here
But if I make the variable global then it works fine.
#include<stdio.h>
int a;
int a = 10;
int main()
{
printf("a is %d \n",a);
return 0;
}
Why is declaring the same global variable twice not an error, but doing that for a local variable is an error?
In C, the statement int a; when made at file scope, is a declaration and a tentative definition. You can have as many tentative definitions as you want, as long as they all match each other.
If a definition (with an initializer) appears before the end of the translation unit, the variable will be initialized to that value. Having multiple initialization values is a compiler error.
If the end of the translation unit is reached, and no non-tentative definition was found, the variable will be zero initialized.
The above does not apply for local variables. Here a declaration also serves as a definition, and having more than one leads to an error.
The other reason I could think of is that un-initialized global variables are stored in the BSS (Block Structured Segment) where are the global variables that are initialized are stored in Data Segment.
I am guessing that there is some kind of a name space resolution and when there is a conflict the variable in the Data segment overrides the one in the Block Structured Segment.
if you were to declare
int a =5
int a = 10
in the global scope (both in the data segment) there would be conflict as expected.
You can't have two global variables with the same name in C program.
C might allow multiple definitions in the same file scope through the tentative definition rule, but in any case all definitions will refer to the same variable.
Local Variable
In C, multiple local variables are not "merged" into one.
All the local variables with the same name will be referring to the different int-sized piece of memory.
#include<stdio.h>
int main()
{
int a;
int a = 10;
printf("a is %d \n",a);
return 0;
}
So when assigning the memory to the redeclaration of the same variable it gives an Error.
Global Variable
In C, multiple global variables are "merged" into one. So you have indeed just one global variable, declared multiple times. This goes back to a time when extern wasn't needed (or possibly didn't exist - not quite sure) in C.
In other words, all global variables with the same name will be converted to be one variable - so your
#include<stdio.h>
int a;
int a = 10;
int main()
{
printf("a is %d \n",a);
return 0;
}
will be referring to the same int-sized piece of memory.

Strange behaviour of extern in c

I am surprised with the following behaviour of extern.
When I run this
#include<stdio.h>
int main()
{
extern int a;
printf("%d\n", a);
return 0;
}
int a=20;
OUTPUT: 20(in gcc Linux 32-bit,which seems OK)
But when I declare the variable a inside main():
#include<stdio.h>
int main()
{
extern int a;
int a=20;
printf("%d\n", a);
return 0;
}
OUTPUT:
extern.c: In function ‘main’:
extern.c:5:9: error: declaration of ‘a’ with no linkage follows extern declaration
extern.c:4:16: note: previous declaration of ‘a’ was here
Why I am getting the error now after changing the scope of a from global to local?Why it now doesn't allow redeclaration of a ,whereas it allowed in previous case.
The point of an extern declaration is to tell the compiler about a global variable or function used by multiple compilation units (.c files), but defined and allocated in a single compilation unit. The extern declaration is placed in a header file included by all units, and a single compilation unit contains the actual definition, therefore seeing both.
Your first example is legal C: you are declaring that a will refer to an externally defined variable, and then proceed to define that variable in the current compilation unit. Normally the extern declaration would be included from a header file and therefore appear to the compiler on top-level, not inside the function, but the compiler doesn't care either way. In other words, here there is no redefinition, only definition following declaration.
Your second example declares a to have external linkage, and then proceeds to define it as a local variable in main. The declaration and the definition are obviously incompatible - if a is a local variable, it cannot be defined and allocated in only one place - it will instead be automatically allocated on the stack every time the function is called. This incompatibility results in the error diagnostic.
In the first example, you are telling to the compiler: "hey, I have this variable called a that is defined somewhere else.", and then you use it. That's fine.
#include<stdio.h>
int main()
{
extern int a;
printf("%d\n", a);
return 0;
}
int a=20;
In the second example, you are also telling to the compiler that there is somewhere in the program a variable called a you want to use, and then you declare a new variable with the same name on your stack. That does not make much sense and the compiler says 2 things: can't find any declaration of variable a in the program and variable a already exists (if you fix the first error, it will).
#include<stdio.h>
int main()
{
extern int a;
int a=20;
printf("%d\n", a);
return 0;
}
When you declare a global variable, its memory is allocated when the program is launched (it's in the binary file and mapped in memory). When you declare a local variable, the memory is allocated on the stack, during the execution of the function. You can't refer with extern to a variable that does not exist yet.

Resources