How is the value of global variable changing in my code? - c

I have declared global variable again after the main function but It still affects main function. I know that C allows a global variable to be declared again when first declaration doesn’t initialize the variable(It will not work in c++). If I assign the value after the main function it still works with two warning in c but gives error in c++.
I have debugged the code but it never reaches the line int a=10;.
#include <stdio.h>
#include <string.h>
int a;
int main()
{
printf("%d",a);
return 0;
}
/*a=10 works fine with following warnings in c.
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'a' [-Wimplicit-int]|
but c++ gives the following error
error: 'a' does not name a type|
*/
int a=10;
The output is 10.

Several things:
The first int a; is a tentative declaration; the second int a = 10; is a defining declaration.
a is declared at file scope, so it will have static storage duration - this means that storage for it will be set aside and initialized at program startup (before main executes), even though the defining declaration occurs later in the source code.
Older versions of C allow for implicit int declaration - if a variable or function call appears without a declaration, it is assumed to have type int. C++ does not support implicit declarations, so you will get an error.

Here
int a; /* global declaration */
compiler treats above statement as just a declaration not definition. It looks for definition of a in other translation units, it finds below main() as
int a=10;
Hence the output 10.
To avoid warnings, declare a with extern storage class for e.g
extern int a;

From C Standard#6.9.2p2
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.....
So, this
int a;
is tentative definition of identifier a.
Couple of points about tentative definition:
If there are no definitions in the same translation unit, then the tentative definition acts as an actual definition with the initializer = 0.
If an actual external definition is found earlier or later in the same translation unit, then the tentative definition just acts as a declaration.
In your program, compiler found the definition of a in the same translation unit:
int a=10;
Hence, you are getting the output 10 when compiling with C compiler.
Now, regarding the error when compiling with C++ compiler:
If you have this statement in your program:
a=10;
This will give error when compile with C++ compiler because you are missing the type specifier which is required. But this code will compile with C complier because, in older version of C (C89/90), if the type specifier is missing then it will default set to int. Of course, you will get warning message when compile with C99 & C11 compiler because this implicit declaration is no longer supported.
If you have this statement in your program:
int a=10;
C++ does not have concept of tentative definitions and int a; is definition in C++. Hence, due to concept of One Definition Rule the C++ compiler will give error - redefinition of 'a'.

all i know, today's c++ compiler cannot run the code:
int a;
int main()
{
printf("%d",a);
return 0;
}
int a=10;
nor
int a;
int main()
{
printf("%d",a);
return 0;
}
a=10;
because c++ detects double declaration of variable.
and
because it cannot initialize a variable outside a method.
error "'a' does not name a type" is because of that (the second) error, c++ expect the first word to be a type for declaration (ex: int, long, char, -etc-), and variable is given.

Related

Does the compiler automatically add an "extern" to a global variable that is not assigned a value in Programe language C? [duplicate]

This question already has answers here:
In C, is it valid to declare a variable multiple times?
(1 answer)
In C,why is multiple declarations working fine for a global variable but not for a local variable?
(3 answers)
Closed last year.
So far I have understood the following:
A variable declaration is the declaration of a type and name of a variable without allocating memory space for it.
A variable definition means that the variable is declared and memory space is allocated for it.
So it has nothing to do with the initialization of the variable, whether you speak of a definition or a declaration.
In C, a declaration is always a definition e.g. if one write int i;.
But there is one exception. If you write extern int i; no memory space is allocated, only the variable is declared.
So int i; is always declaration and definition at the same time. But extern int i; is just declaration.
Is it true that in C you can declare a variable as often as you want, but you can only define the variable once?
I ask because I've tried the following and the compiler results confuse me. I use gcc and don't set the -std flag.
Neither this program:
int i;
int i;
void main(void){
i = 2;
}
nor this program:
int i=0;
int i;
void main(void){
i = 2;
}
lead to problems. The compiler compiles both without error. I would have expected since I didn't use the "extern" keyword here that the compiler would say something like "error: multiple definition".
But it doesn't give an error message. Is it possible that the compiler automatically writes an "extern" before all global defined "int i;" if I don't initialize them at the same time?
Isn't it then superfluous for the programmer to ever use the extern keyword for variables since the compiler will do that automatically anyway?
I think my considerations are confirmed by the following behavior. The following programs return errors:
int i;
i=0;
void main(void){
i = 2;
}
leads to:
"warning: data definition has no type or storage class
i=0;
warning: type defaults to 'int' in declaration of 'i' [-Wimplicit-int]"
and
float i;
i=0;
void main(void){
i = 2;
}
leads to:
"warning: data definition has no type or storage class
i=0;
warning: type defaults to 'int' in declaration of 'i' [-Wimplicit-int]
error: conflicting types for 'i'
note: previous declaration of 'i' was here
float i;"
So to me again it looks like there is an implicit "extern" before the first int i; respectively float i; is written because they are not assigned a value. As a result, no storage space is allocated for i.
But there is no other file in which storage space is allocated for i. Therefore there is no definition for i and the compiler therefore thinks in the 2nd line that i should be defined here.
Therefore there are no problems with the 1st program because the automatic type assignment fits, but with the 2nd program it no longer fits, which is why an error is returned.
The following program also throws an error:
void main(void){
int i;
int i;
}
If I write the declaration (and thus also the definition) in a scope, the compiler returns the following error message.
"error: redeclaration of 'i' with no linkage int i;
note: previous declaration of 'i' was here int i;"
I can only explain it again with the fact that the compiler does not automatically set an "extern" before a variable that is not a global variable and therefore there are 2 definitions here.
But then I ask myself why is it called redeclaration and not redefinition or multiple definition?
It would be very nice if someone could confirm my assumptions or enlighten me on how to understand it correctly. Many Thanks!
A variable declaration is the declaration of a type and name of a variable without allocating memory space for it.
Even if memory is reserved for an object, it a declaration. We do not exclude definitions from declarations; there are declarations that are definitions and declarations that are not definitions.
The declaration x = 3; causes memory to be reserved for x, but it also makes the name and type of x known, so it declares x.
So int i; is always declaration and definition at the same time.
Not quite. Inside a function, int i; is a definition. Outside of a function, int i; is a tentative definition. This is a special category that was necessary due to the history of C development. The language was not designed all at once with foresight about how it would be used. Different implementors tried different things. When a standard for the C language was developed, the committee working on it had to accommodate diverse existing uses.
When there is a tentative definition, the program can still supply a regular definition later in the translation unit. (The translation unit is the source file being compiled along with all the files included in it.) If the program does not supply a regular definition by the end of the translation unit, then the tentative definition becomes a regular definition as if it had an initializer of zero, as in int i = 0;.
Some C implementations treat multiple tentative definitions of an identifier in different translation units as referring to the same object. Some C implementations treat them as errors. Both behaviors are allowed by the C standard.
Is it true that in C you can declare a variable as often as you want, but you can only define the variable once?
Not always. Variables with no linkage (declared inside a function without static or extern) may not be declared more than once. (An identical declaration can appear inside a nested block, but this declares a new variable with the same name.)
Repeated declarations must have compatible types, and there are additional rules about which repeated declarations are allowed. For example, an identifier may not be declared with static after it has been declared with extern.
The compiler compiles both without error.
As described above, int i; outside a function is a tentative definition. Initially, it acts only as a non-definition declaration. So it may be repeated, and it may be replaced by a regular definition.
So to me again it looks like there is an implicit "extern" before the first int i;
No, there is not. int i; is a tentative definition, and it has nothing to do with the error messages you are getting. The error messages “data definition has no type or storage class” and “type defaults to 'int' in declaration of 'i'” are from the i=0;. This is a statement, not a declaration, but the C grammar does not provide for statements outside of functions. Outside of functions, the compiler is looking for only declarations. So it expects to see a type, as in int i=0;. The first message tells you the compiler does not see a type or a storage class. The second message tells you that, since it did not see a type, it is assuming int. This is a relic of old behavior in C where the int type would be taken as a default, so it could be left off. (Do not use that in new C code.)
The following program also throws an error:
Inside a function, int i; is a definition, so two of them causes multiple definitions of i.

Multiple definitions gives no errors or warnings in either gcc or clang

If I did
#include <stdio.h>
int a; //definition
int a; //definition
int a; //definition
int a; //definition
int a; //definition
int main() {
return 0;
}
For example, I would get no errors or warnings from either gcc or clang despite defining a variable multiple times. Why? I thought I was allowed to declare a variable as many times as I wanted to, but could only define it once?
This is a tentative definition. That is each declaration of a file scope variable without an initializer is considered as a declaration not as a definition. The definition is implicitly generated at the end of the translation unit with an initializer equal to 0.
From the C Standard (6.9.2 External object definitions)
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.

Do you need to use extern with incomplete types?

Do you need absolutely need to use extern with an incomplete type for instance int a[]; for it to use an array a definition in a linked file? My logic is that it doesn't reserve memory so it's not a definition but a declaration (like a function prototype, which also doesn't require extern for the compiler to leave it to the linker implicitly). I would test it myself but I can't currently.
Since you ask do you absolutely need to use extern with a declaration of an identifier with a nominally incomplete type, the answer is technically “no,” for two reasons:
The C standard is voluntary. Nothing requires you to obey it.
If you are using the C standard, and you declare int a[]; externally (outside any function) in one translation unit and int a[5] = { 3, -7, 24, 5, 7 }; inside another translation unit, and you use a in the program, the behavior is not defined by the C standard. That is, the C standard “allows” you to do it but does not define the result.
I will come back to explain why the latter is not defined. First, let’s see why the answer to the question you actually wanted to ask is “yes.”
If instead you ask do you need to use extern to get a defined result, and presumably the result that you want, then the answer is “yes.” If you declare int a[]; in a translation unit, it is a tentative definition per C 2018 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.
This means that, if you declare int a[]; externally and do not otherwise define it in the same translation unit (the source file with all the included files), it is as if you wrote int a[] = { 0 };, which defines it to be an array of one element. So it is effectively a definition, not just a declaration.
To prevent it from being a tentative definition and becoming a definition, you need to declare it as extern int a[];.
If you do not, then you will have this definition in one source file and the definition in the other file you are linking. Then, if you use a in the program, C 2018 6.9 5 is violated:
… If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.
This “shall” is part of the semantics of external definitions, not part of the constraints, so it is governed by 4 2:
If a “shall” or “shall not” requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined…
This explains my second bullet point above.
Adding to that, though, this is one instance where the “undefined behavior” of the C standard is actually in some common use. In common Unix tools, a tentative definition in one translation unit is resolved as desired with a non-tentative definition in other translation units. So “no” is also the answer to your intended question provided you are using tools that support this.
I used repl.it (clang) and it has an interesting result.
Scenario 1:
#include<stdio.h>
int a[5] = {1,2,3};
#include<stdio.h>
extern int a[];
int main()
{
printf("%d", a[1]); // '2'
printf("%lu", sizeof(a)); // error 'invalid application of sizeof to incomplete type int []'
return 0;
}
Scenario 2:
#include<stdio.h>
int a[5] = {1,2,3};
#include<stdio.h>
int a[];
int main()
{
printf("%d", a[1]); // '2'
printf("%lu", sizeof(a)); // error 'invalid application of sizeof to incomplete type int []'
return 0;
}
Scenario 3:
#include<stdio.h>
extern int a[];
int main()
{
printf("%d", a[1]); // '0'
printf("%lu", sizeof(a)); // error 'invalid application of sizeof to incomplete type int []'
return 0;
}
Scenario 4:
#include<stdio.h>
int a[5] = {1,2,3};
#include<stdio.h>
extern int a[2];
int main()
{
printf("%d", a[1]); // '2'
printf("%lu", sizeof(a)); // '8'
return 0;
}
Scenario 5:
#include<stdio.h>
int a = 2;
#include<stdio.h>
int a;
int main()
{
printf("%d", a); // '2'
return 0;
}
Only static int a; will produce '0' but int a; appears to be taken implicitly as extern int a; although (extern) int a = 1; would be taken as a multiple definition error if initialised in the other file (if not initialised in the other file i.e. int a;, the other file will use the overridden extern initialisation in the main file). (extern) int a; in one file and int a; in the other file causes a single zero-initialised uninitialised int a to be used in both files. extern int a; and nothing in the other file causes an error.
Refer to this as to why.

Why 'extern' storage class works differently in functions?

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?

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