In C, is it valid to declare a variable multiple times? - c

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.

Related

Why am i not getting re-declaration error in global scope [duplicate]

This question already has answers here:
About Tentative definition
(2 answers)
Closed 1 year ago.
This code gives me no re-declaration error.
#include<stdio.h>
int i;
int i = 27;
int main()
{
printf("%d",i);
return 0;
}
According to me I declared and defined an uninitialised global variable with 0 as default value.
And later re-declared, redefined and assigned it 27 value.
I was expecting it to give a re-declaration error because both i's are in same scope(global).
But I'm not getting any error why?
But below code gives me a re-declaration error as expected because of defining them in same scope.
#include<stdio.h>
int main()
{
int i;
int i = 27;
printf("%d",i);
return 0;
}
At file scope, this:
int i;
Is a tentative definition since there is no initializer. It will be considered an external definition if no other definition appears.
When you then do this:
int i = 27;
This constitutes an external definition for i which matches the prior tentative definition.
These terms are defined in section 6.9.2 p1 and p2 of the C standard:
1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external
definition for the identifier.
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 initialize requal to 0.
Your second code snippet defines a variable in block scope (not file scope), then defines it again in the same scope. That constitutes a variable redefinition.
In C this declaration in the file scope without an initializer
int i;
is a declaration of a variable and not its definition, So the next declaration
int i = 27;
is the definition of the variable.
You may declare a variable without its definition in a file scope several times though the declarations can be redundant.

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.

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?

regarding the use of extern keyword

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.

Why does redefining a static global variable give a compile-time error when redefining a global variable does not?

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.

Resources