Strange behaviour of extern in c - 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.

Related

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.

Problems compiling program with extern variable

Inside main() function when I create a separate block (new pair of curly braces) like this one-:
int main(void){
int x = 10;
{
extern int y;
printf("\tNo. is %d\n", y);
int y = 20;
}
}
When I compile this code I come across an error :
test.c: In function ‘main’:
test.c:12:9: error: declaration of ‘y’ with no linkage follows extern declaration
int y = 20;
test.c:9:16: note: previous declaration of ‘y’ was here
extern int y;
But
If the definitaion for int y is placed at end of the main function the code compiles and run perfectly okay.
What could be reason behind this error? According to my book if a variable is declared as extern then we can use it before defining it and the compiler will search in the whole file for definition of the variable.
C distinguishes variables in file scope (= outside any function) and variables in a local scope.
The y-variable that you declare with extern and use in the printf refers to a variable in file scope. That variable is only declared and must be "defined" elsewhere. That is storage must be allocated for it.
If you you have the second declaration of y inside any of the {} this is a local variable that is different from the file scope variable. If it is outside, it is a declaration of a file scope variable and a "tentative definition" of that file scope variable. So in this later case you have a declaration that is visible where the variable is used, and somewhere else a definition such that storage is provided, and everything works fine.
You can't declare a variable with identical name twice in a block with same block scope.
yes there is problem there when you used extern there. that means this int y is defined globally in the same file or different file. but without defining y (globally) you are printing that extern value that's why its error for linker

How are extern variables defined?

I have a few doubts regarding the use of extern keyword with variables in C. I did go through the links related to this question. However, there are still a few things that I didn't gather very well
#include<stdio.h>
main( )
{
extern int i;
printf ( "\n%d ",i) ) ;
}
int i = 31 ;
In the above code, how does I get printed before its definition statement?
Now in the following code:
#include<stdio.h>
int x = 21 ;
main( )
{extern int i;
i=20;
printf ( "\n%d ", i ) ;
}
Isn't the statement "i=20;" a definition statement? I get an error for this. Is it because i'm trying to change the variable that is defined in some other source file? If this is the case how is the statement "int i=31;" in the top most code snippet right to use?
Also, I read, "int i;" is a definition. I don't really follow how.
In your first program, the print statement is printing the value of i based on the extern int i declaration. This is similar to calling a function based on its prototype declaration without seeing its definition. The compiler generates code to retrieve the value in the global variable named i. The symbol is resolved to the correct variable and address at link time.
In your second program, no definition for i is provided, only the extern int i declaration, and the attempt to set its value with i = 20. At link time this fails, since there is no definition, and so the attempt to resolve the reference to the global variable fails. Changing i = 20 to int i = 20 instead creates a local variable named i within the scope of the function main(), and is no longer referencing the globally declared extern int i.
When int i; is used at global scope, it is treated as a declaration and may be treated as a kind of definition. A global variable declared with an initializer, like:
int i = 20;
is always treated as a definition. Only one definition of this type is allowed, even if each is using the same initializer value. However,
int i;
is treated like a declaration. If more than one of these declarations appear, they are all treated as declarations of the same variable. At the same time, if no declaration with an initializer is present, this variable is implicitly defined with an initializer of 0, and the linker will be able to resolve references to it.
Declaration of a variable/function simply declares that the variable/function exists somewhere in the program but the memory is not allocated for them.
Coming to the definition, when we define a variable/function, apart from the role of declaration, it also allocates memory for that variable/function.
So in the 1st program, int i = 31 outside the main() defines the variable i. This variable is then merely declared as an extern variable inside the main(). Hence the program works.
In the 2nd program, without defining the variable i elsewhere, it is directly declared as an extern variable. Hence it gives an error.
In the second program you haven't defined the variable anywhere , not outside the main and instead you have defined it inside main so you are getting that error.
But in the first program you are defining the variable once and that is outside the main so you don't get that error.Program looks outside the main and find its definition.
int i; is a variable definition declaration both for AUTO variables (i.e inside main or any function ) but not for the external variables for extern variables you have to declare it like :
extern int i;
and then define outside main like this :
int i;
The idea behind extern is that you tell the compiler you're going to use a variable that was already defined outside the scope of where it is used.
It doesn't make much sense in your second case as there i is defined as a local variable.
If you were to define it as a global, like you do in the first case, it would be fine.
If I can demonstrate the use of extern with a very simple (contrived) case:
main.c:
#include <stdio.h>
extern int a; /* we tell the compiler that a will come from outside the scope of where it is used */
int main()
{
printf("%d\n", a);
return 0;
}
source.c:
int a = 3; /* we define a here */
You then compile both .c files like this:
$ gcc main.c source.c
This outputs 3.
You might also want to have a read at this article.

working of extern keyword

I have defined a variable in one file and declared it in another file using the extern keyword. but i have declared it with different datatype..
file1.c
char i;
main()
{
foo();
}
file2.c
void foo()
{
extern int i;
i = 130;
printf("%d", i);
}
In the above program, memory is allocated for 'i' in the main function as char datatype.
And the answer after executing should be negative(-127). But it prints 130. Whatever the value assigned to 'i' in the foo() function is printed not only 130.
"I have defined a variable in one file and declared it in another file using the extern keyword". That is just not true. char i and in main is not in any way related to int i in foo.
The variable you define as char i inside main has no linkage. It cannot be referred to by name from anywhere else in the program besides that main function. There's simply no way to do it in C. It is a local variable.
Your extern int i; declaration inside foo has absolutely no relation to that local char i; variable from main. It declares a completely different, independent variable i. The extern int i declaration in foo refers to a variable i with external linkage, i.e to a global variable i. You are still required to define that i somewhere. That i will have to be defined at file scope (since this is the only way to define a variable with external linkage). If you don't define it your program will not compile.
Entities with external linkage and entities with no linkage live in completely separate worlds. They know nothing about each other and never interact or interfere with each other.
As I said above, the code you posted in your question simply won't compile. The linker will tell you that you forgot to define the int i variable with external linkage that which you declared in used in foo. Yet, your post suggests that the code compiled successfully. This would simply mean that the code you posted is either inaccurate or incomplete. If the code is accurate, then somewhere else in your code you also defined a global variable int i. This is the global variable your foo works with, while the char i in main has absolutely nothing to do with anything.
What you have now, after your edit, is an invalid program again, albeit for a different reason. You defined a global variable char i in file1.c and then you attempted to link to it in file2.c. However, in file2.c you lied to the compiler by telling it that the global variable i has type int, while in reality it has type char.
The behavior of such program is undefined. It is illegal to use inconsistent types when declaring an entity with external linkage. If you declared a global variable of type char in one translation unit, you are not allowed to declare the same global variable with any other type in other translation units.
Some compilers might catch your error and refuse to compile the program. Most C compilers will not catch this error though. Instead the will produce a program that causes undefined behavior. This is what you got in your case. In your case that undefined behavior happened to manifest itself by printing 130 after you assigned 130 to your variable. Why you suddenly find it strange I don't know. Why you expect it to print -127 I don't know either.
Analysis — What is wrong with the code
In your code:
file1.c
int main(void)
{
char i; // Warning: unused variable
foo();
}
file2.c
void foo(void)
{
extern int i;
i = 130;
printf("%d", i);
}
The variable i in main() is wholly unrelated to the i referred to in foo(). The variable i in main() is strictly local to main() and is not passed to foo(); foo() is unable to see that i.
On Mac OS X 10.7.5 with GCC 4.7.1, the files compile separately, but they can't be linked because there is no definition for variable i.
If we move the variable i outside of main(), then the program links:
file2.h
extern void foo(void);
file1.c
#include <stdio.h>
#include "file2.h"
char i = 'A';
int main(void)
{
printf("i = %d (%c)\n", i, i);
foo();
printf("i = %d (%c)\n", i, i);
return(0);
}
file2.c
#include "file2.h"
#include <stdio.h>
void foo(void)
{
extern int i;
i = 130;
printf("i = %d (%c)\n", i, i);
}
This now links and runs, but it invokes undefined behaviour. The linker does not notice that the size of i should be sizeof(int) but is sizeof(char). The assignment in foo(), though, goes trampling out of bounds of the i that is allocated. It doesn't cause visible damage in this program, but that's pure luck.
When I run it on a little-endian machine (Intel x86/64), the output is:
i = 65 (A)
i = 130 (?)
i = -126 (?)
When I ran it on a big-endian machine (SPARC), I got a different result (which isn't very surprising; the behaviour is undefined, so any result is valid):
i = 65 (A)
i = 130 (?)
i = 0 ()
The most significant byte of the 4-byte integer was written over the only byte of the 1-byte character, hence the zero in the third line out output. Note too that it was lucky that the character was allocated an address that was a multiple of 4 bytes; that was not guaranteed and a misaligned address for an int would have caused a bus error (though experimentation suggests that it doesn't happen on SPARC even when i is forced onto an odd address; I'm not sure what is happening).
Synthesis — Correct Handling of extern
The correct way to handle this is to avoid writing external variable declarations such as extern int i; in any source file; such declarations should only appear in a header that is used everywhere the variable needs to be used.
file2.h
extern char i;
extern void foo(void);
With that change in place (and the corresponding extern int i; removed), then the output is self-consistent:
i = 65 (A)
i = -126 (?)
i = -126 (?)
Note the role of the header in keeping both file1.c and file2.c in line with each other. It ensures that the definition in file1.c matches the declaration in file2.h, and the use of file2.h in file2.c ensures that the declaration used there is correct, too. See also What are extern variables in C.
The extern declaration inside foo has absolutely no relation to that local char variable.
As far as I know, extern can be put in front of a variable declaration.
Perhaps this is a bit more confusing.
Doing that tells the compiler you want to share the variable between different .c files.
However, there has to be one .c file where the variable is declared without the extern in front of it.
Note: You're example wouldn't work anyway, as extern has no effect on local variables.
extern is a way to tell the compiler some variable you are using is implemented in another object file that you promise that you will link at link time. It is often used in library header files that involve global variables to tell the compiler that the variable is actually implemented in the library file.
Ex:
file1.c:
int thing;
int bork(int val) {
return thing += val
}
file2.c
extern int thing
int main() {
thing + 2;
}
In the case when i is declared in one file as global variable then only it has external linkage.
Linker only looks for the symbol i which will be resolved at link time ,but types are not resolved by linker so I think it's Undefined Behavior when i is of different type and defind as global variable in one file and extern declaration is done in other file.
You are LYING to the compiler about the size of i. The compiler goes by what you have told it in foo, so you are getting the integer representation. Unfortunately, it won't really work reliably, because you have also written three bytes beyond your char i;
If you add char j = 11; and then print j in main after you call foo();, you'll notice that j is not 11 any more.
Lying to compilers is a bad idea. You get found out sooner or later!
In file1.c, i is defined (and declared implicitly) globally.
char i;
main()
{
foo();
}
file2.c extern is used with a variable i, it’s only declared not defined.
extern char i;
void foo()
{
printf("%d", i);
}

extern variables in C and their scope

I am trying to understand this code:
#include<stdio.h>
int main()
{
extern int a;
printf("%d\n", a);
return 0;
}
int a=20;
When I run it, the value of a is 20. Yet this should be impossible, since the global variable a is defined at the bottom.
An extern declaration can only be used with variables that are global. It tells the compiler that the global variable is defined elsewhere, and asks the linker to figure it out.
In your code, extern int a refers to the a defined at the bottom of your example. It could have been equally well defined in a different translation unit.
As others have pointed out, the initialization of a takes place before main() is entered.
Not a problem. By declaring the variable as extern you are promising the linker it is defined else where part of current or other source files at global scope.
Initialization of global variables occurs before main() is called.
So even if the initialization a = 20 is located beneath the implementation of main(), it's always executed first, so it can be used at program start (assuming you fittingly declared the variable in scopes where it's to be used using extern int a).

Resources