I had such a question:What is the difference between
int i;
for(i = 0; i < n; i++)
and
for(int i = 0; i < n; i++)
Does this influence memory and time?
C 2018 6.2.1 2 says:
For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text called its scope…
6.2.1 4 says:
Every other identifier [paragraph 3 discussed labels] has scope determined by the placement of its declaration (in a declarator or type specifier)… If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block…
Which portions of C source code form blocks is stated individually in several places.
6.8.2 1:
A compound statement [source code inside { and }] is a block.
6.8.4 3:
A selection statement [if, if … else, and switch] is a block whose scope is a strict subset of the scope of its enclosing block. Each associated substatement is also a block whose scope is a strict subset of the scope of the selection statement.
6.8.5 5:
An iteration statement [while, do … while, and for] is a block whose scope is a strict subset of the scope of its enclosing block. The loop body is also a block whose scope is a strict subset of the scope of the iteration statement.
Thus for (int i = 0; i < n; i++) declares an i whose scope is limited to the for statement. This i cannot be used after the for statement.
In contrast, the int i; before the for statement declares an i that can be used throughout the block it is in, which is the { … } that it is enclosed in.
Assuming you have written this code in the main function,
int i;
for(i = 0; i < n; i++)
In the case above, i is a local variable of the main function. If the value of i is updated in the loop it will remain updated even after the loop ends. i is not destroyed after the for loop. This is useful in some programs where you need the value of i after the loop for later use.
for(int i = 0; i < n; i++)
In the case above, i is a local variable of the for loop. i will be destroyed once the loop is over.
Related
In the following code, why is the variable i not assigned the value 1?
#include <stdio.h>
int main(void)
{
int val = 0;
switch (val) {
int i = 1; //i is defined here
case 0:
printf("value: %d\n", i);
break;
default:
printf("value: %d\n", i);
break;
}
return 0;
}
When I compile, I get a warning about i not being initialized despite int i = 1; that clearly initializes it
$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
printf("value %d\n", i);
^
If val = 0, then the output is 0.
If val = 1 or anything else, then the output is also 0.
Please explain to me why the variable i is declared but not defined inside the switch. The object whose identifier is i exists with automatic storage duration (within the block) but is never initialized. Why?
According to the C standard (6.8 Statements and blocks), emphasis mine:
3 A block allows a set of declarations and statements to be grouped
into one syntactic unit. The initializers of objects that have
automatic storage duration, and the variable length array declarators
of ordinary identifiers with block scope, are evaluated and the values
are stored in the objects (including storing an indeterminate value
in objects without an initializer) each time the declaration is
reached in the order of execution, as if it were a statement, and
within each declaration in the order that declarators appear.
And (6.8.4.2 The switch statement)
4 A switch statement causes control to jump to, into, or past the
statement that is the switch body, depending on the value of a
controlling expression, and on the presence of a default label and the
values of any case labels on or in the switch body. A case or default
label is accessible only within the closest enclosing switch
statement.
Thus the initializer of variable i is never evaluated because the declaration
switch (val) {
int i = 1; //i is defined here
//...
is not reached in the order of execution due to jumps to case labels and like any variable with the automatic storage duration has indeterminate value.
See also this normative example from 6.8.4.2/7:
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17; /* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with
automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero
value, the call to the printf function will access an indeterminate
value. Similarly, the call to the function f cannot be reached.
In the case when val is not zero, the execution jumps directly to the label default. This means that the variable i, while defined in the block, isn't initialized and its value is indeterminate.
6.8.2.4 The switch statement
A switch statement causes control to jump to, into, or past the statement that is the
switch body, depending on the value of a controlling expression, and on the presence of a
default label and the values of any case labels on or in the switch body. A case or
default label is accessible only within the closest enclosing switch statement.
Indeed, your i is declared inside the switch block, so it only exists inside the switch. However, its initialization is never reached, so it stays uninitialized when val is not 0.
It is a bit like the following code:
{
int i;
if (val==0) goto zerovalued;
else goto nonzerovalued;
i=1; // statement never reached
zerovalued:
i = 10;
printf("value:%d\n",i);
goto next;
nonzerovalued:
printf("value:%d\n",i);
goto next;
next:
return 0;
}
Intuitively, think of raw declaration like asking the compiler for some location (on the call frame in your call stack, or in a register, or whatever), and think of initialization as an assignment statement. Both are separate steps, and you could look at an initializing declaration in C like int i=1; as syntactic sugar for the raw declaration int i; followed by the initializing assignment i=1;.
(actually, things are slightly more complex e.g. with int i= i!=i; and even more complex in C++)
Line for initialization of i variable int i = 1; is never called because it does not belong to any of available cases.
The initialization of variables with automatic storage durations is detailed in C11 6.2.4p6:
For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.
I.e. the lifetime of i in
switch(a) {
int i = 2;
case 1: printf("%d",i);
break;
default: printf("Hello\n");
}
is from { to }. Its value is indeterminate, unless the declaration int i = 2; is reached in the execution of the block. Since the declaration is before any case label, the declaration cannot be ever reached, since the switch jumps to the corresponding case label - and over the initialization.
Therefore i remains uninitialized. And since it does, and since it has its address never taken, the use of the uninitialized value to undefined behaviour C11 6.3.2.1p2:
[...] If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
(Notice that the standard itself here words the contents in the clarifying parenthesis incorrectly - it is declared with an initializer but the initializer is not executed).
Why doesn't this code give a redefinition error:
int main(void)
{
for (int i = 0; i < 2; i++)
{
int number = 5;
}
}
while this code does:
int main(void)
{
int number = 5;
int number = 5;
}
In the first code, the scope of number begins when execution reaches the opening of the for loop and ends when execution reaches the closing of the for loop. In this case, the body of the for loop is executed two times, and number is created and destroyed two times, and has two disjoint scopes. In the second code, the scope aren't disjoint as previous number is still persisting it's lifetime when the second one is defined, and they are in the same scope, so it's a re-definition error.
Redfinition errors are a compile-time issue, not a runtime issue. You don’t get a redefinition error in the loop because you only have one declaration for number within that scope.
In the second snippet you have two declarations for number within the same scope, hence the error.
Basically, creating a variable inside a loop will make the variable's scope stay in this very loop. "number" does not exist outside of your loop.
I have compiled following program using gcc prog.c -Wall -Wextra -std=gnu11 -pedantic command on GCC compiler. I wondered, it is working fine without any warnings or errors.
#include <stdio.h>
int main(void)
{
for (int i = 0; i == 0; i++)
{
printf("%d\n", i);
long int i = 1; // Why doesn't redeclaration error?
printf("%ld\n", i);
}
}
Why compiler doesn't generate redeclaration variable i error?
From standard §6.8.5.5 (N1570)
An iteration statement is a block whose scope is a strict subset of
the scope of its enclosing block. The loop body is also a block whose
scope is a strict subset of the scope of the iteration statement.
Emphasis added
In C language, the scope of statement is nested within the scope of for loop init-statement.
According to Cppreference :
While in C++, the scope of the init-statement and the scope of
statement are one and the same, in C the scope of statement is nested
within the scope of init-statement.
According to stmt:
The for statement
for ( for-init-statement conditionopt ; expressionopt ) statement
is equivalent to
{
for-init-statement
while ( condition ) {
statement
expression ;
}
}
except that names declared in the for-init-statement are in the same declarative-region as those declared in the condition,
and except that a continue in statement (not enclosed in another
iteration statement) will execute expression before re-evaluating
condition.
You have to set -Wshadow to get warnings on shadowed variables. Variable shadowing is allowed in C.
But this is an edge case. A var declared in the head of a for construction is not outside the brackets, because it has no scope after the construction.
This is not equivalent:
int i;
for( i = 0; …)
{ … }
// is is still in scope but wouldn't if declared in the head of for
But, it is not inside the brackets, too.
for( i = 0; …)
{
int i; // this would be strange, because i is used before it is declared.
…
}
The best approximative replacement of the code is this:
{
int i;
for( i = 0; …)
{
…
}
} // i loses scope
So it is no redeclaration, but a shadowing declaration inside the loop's body.
Why compiler doesn't generate redeclaration variable i error?
From C Standards#6.2.1p4 Scopes of identifiers
Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block. If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.
From C standards#6.8.5p5 Iteration statements
An iteration statement is a block whose scope is a strict subset of the scope of its enclosing block. The loop body is also a block whose scope is a strict subset of the scope of the iteration statement.
So, in this code:
for (int i = 0; i == 0; i++)
{
printf("%d\n", i);
long int i = 1; // Why doesn't redeclaration error?
printf("%ld\n", i);
}
the scope of identifier with name i is overlapping and in this name space, the i declared in for (int i = 0; i == 0; i++) has outer scope and the one declared within loop body long int i = 1; has inner scope.
Within the loop body, after this statement:
long int i = 1;
the i declared in the outer scope is not visible and printf() printing the value of i visible in the inner scope which is 1.
This behavior is also known as Variable Shadowing which occurs when a variable declared within a certain scope has the same name as a variable declared in an outer scope.
C language allows variable shadowing and that's why the compiler does not throw any error for this. However, in gcc compiler, if you use -Wshadow option you will get a warning message - declaration shadows a local variable.
For further verification i checked this code in visual studio 2008 for the prog.c file. I found that the compiler does give error in the line for (int i = 0; i == 0; i++) . The compiler expects declaration of i to be in the beginning of the program itself. This behavior is correct for a C file. If the declaration is moved to the beginning of the program then there are no errors as expected. All scope related issues are solved.
If i try this code as prog.cpp file, then the compiler does give an error for redeclaration. This is also an expected behavior.
So i conclude this has to do with the gcc compiler, does any flag/parameters that is used for compiling/building the exe results in this behavior for the gcc compiler.
Can rsp post the make file details for further verification?
In the following code, why is the variable i not assigned the value 1?
#include <stdio.h>
int main(void)
{
int val = 0;
switch (val) {
int i = 1; //i is defined here
case 0:
printf("value: %d\n", i);
break;
default:
printf("value: %d\n", i);
break;
}
return 0;
}
When I compile, I get a warning about i not being initialized despite int i = 1; that clearly initializes it
$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
printf("value %d\n", i);
^
If val = 0, then the output is 0.
If val = 1 or anything else, then the output is also 0.
Please explain to me why the variable i is declared but not defined inside the switch. The object whose identifier is i exists with automatic storage duration (within the block) but is never initialized. Why?
According to the C standard (6.8 Statements and blocks), emphasis mine:
3 A block allows a set of declarations and statements to be grouped
into one syntactic unit. The initializers of objects that have
automatic storage duration, and the variable length array declarators
of ordinary identifiers with block scope, are evaluated and the values
are stored in the objects (including storing an indeterminate value
in objects without an initializer) each time the declaration is
reached in the order of execution, as if it were a statement, and
within each declaration in the order that declarators appear.
And (6.8.4.2 The switch statement)
4 A switch statement causes control to jump to, into, or past the
statement that is the switch body, depending on the value of a
controlling expression, and on the presence of a default label and the
values of any case labels on or in the switch body. A case or default
label is accessible only within the closest enclosing switch
statement.
Thus the initializer of variable i is never evaluated because the declaration
switch (val) {
int i = 1; //i is defined here
//...
is not reached in the order of execution due to jumps to case labels and like any variable with the automatic storage duration has indeterminate value.
See also this normative example from 6.8.4.2/7:
EXAMPLE In the artificial program fragment
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17; /* falls through into default code */
default:
printf("%d\n", i);
}
the object whose identifier is i exists with
automatic storage duration (within the block) but is never
initialized, and thus if the controlling expression has a nonzero
value, the call to the printf function will access an indeterminate
value. Similarly, the call to the function f cannot be reached.
In the case when val is not zero, the execution jumps directly to the label default. This means that the variable i, while defined in the block, isn't initialized and its value is indeterminate.
6.8.2.4 The switch statement
A switch statement causes control to jump to, into, or past the statement that is the
switch body, depending on the value of a controlling expression, and on the presence of a
default label and the values of any case labels on or in the switch body. A case or
default label is accessible only within the closest enclosing switch statement.
Indeed, your i is declared inside the switch block, so it only exists inside the switch. However, its initialization is never reached, so it stays uninitialized when val is not 0.
It is a bit like the following code:
{
int i;
if (val==0) goto zerovalued;
else goto nonzerovalued;
i=1; // statement never reached
zerovalued:
i = 10;
printf("value:%d\n",i);
goto next;
nonzerovalued:
printf("value:%d\n",i);
goto next;
next:
return 0;
}
Intuitively, think of raw declaration like asking the compiler for some location (on the call frame in your call stack, or in a register, or whatever), and think of initialization as an assignment statement. Both are separate steps, and you could look at an initializing declaration in C like int i=1; as syntactic sugar for the raw declaration int i; followed by the initializing assignment i=1;.
(actually, things are slightly more complex e.g. with int i= i!=i; and even more complex in C++)
Line for initialization of i variable int i = 1; is never called because it does not belong to any of available cases.
The initialization of variables with automatic storage durations is detailed in C11 6.2.4p6:
For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.
I.e. the lifetime of i in
switch(a) {
int i = 2;
case 1: printf("%d",i);
break;
default: printf("Hello\n");
}
is from { to }. Its value is indeterminate, unless the declaration int i = 2; is reached in the execution of the block. Since the declaration is before any case label, the declaration cannot be ever reached, since the switch jumps to the corresponding case label - and over the initialization.
Therefore i remains uninitialized. And since it does, and since it has its address never taken, the use of the uninitialized value to undefined behaviour C11 6.3.2.1p2:
[...] If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
(Notice that the standard itself here words the contents in the clarifying parenthesis incorrectly - it is declared with an initializer but the initializer is not executed).
{
int i;
for(i=0;i<5;i++)
{
int i=10;
printf("%d",i);
}
}
I have two questions
Why is there no redeclaration error for i?
why output will be 10 5 times and not 10 1 time?
It all has to do with the scope of an identifier. An identifier is simply a name given to an entity (object, function, typedef name and so on) in C and, as per C11 6.2.1 /1:
The same identifier can denote different entities at different points in the program.
The scope of an entity is described in /2 of that section:
For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text called its scope.
And /4 covers your specific case:
If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope).
In other words, something like:
{
int i = 42;
printf ("%d ", i);
{
int i = 17;
printf ("%d ", i);
}
printf ("%d\n", i);
}
is perfectly valid, and will print 42 17 42. That's because the identifier i inside the inner block is in its own scope, which ends at the first closing brace.
In your particular case, you can think of it as:
{
int i; \
for(i=0;i<5;i++) > outer i scope
{ /
int i=10; \
printf("%d",i); > inner i scope
} /
}
The inner int i=10 effective hides the outer i for the duration of the body of the for loop. That's why it prints a lot of 10s rather than 0..4.
The scope of the inner i finishes at the closing brace of the for loop body so that, when the continuation condition of the for loop is checked, it once again sees the outer i. That's why it loops five times rather than once.
In your code
int i; is in outer block scope.
int i=10; is in the inner block scope for the for loop
If we visualize the same, we can come up with something like'
{ //---------------------------|
int i; |
for(i=0;i<5;i++) |
{ |
int i=10; //-------| inner scope |> Outer scope
printf("%d",i); //-------| |
} |
} //----------------------------|
The case here is, the inner i will shadow the outer i. These two are considered seperate variable (based on their scope).
In other words, the i which is defined and present inside the block (inner scope) will have more preference (over the variable in outer scope). So, the inner i value will get printed.
OTOH, the loop counter variable i remains at the outer scope. It's value is not altered through the assignement inside the block. So, rightly it loops for 5 times, as asked.
Related: From C11 standard, chapter §6.2.1, paragraph 4,
..[..].. If an identifier designates two different entities in the same name
space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.
So, to answer your questions,
Why is there no redeclaration error for i?
Because two is are treated as seperate variable, despite being named same.
why output will be 10 5 times and not 10 1 time?
Because, the outer i, used as the counter, is not changed from inside the loop, only the loop increment condition is altering that value.
You aren't re-declaring i since the second declaration of i ( 'int i =10' ) is inside a loop . That's means that in your example the variable is destroyed and recreated for each iteration of the loop .
The scope of variables created in any compound statement is limited to the compound statement itself .
Ps : If you wanted to stop the loop by changing the value of i
{
int i;
for(i=0;i<5;i++)
{
i=10;
printf("%d",i);
}
}
If you ask your compiler nicely (gcc -Wshadow) it will warn you
echo -e '#include <stdio.h>\nvoid f(void) { int i; for (i = 0; i < 5; i++) { int i = 10; printf("%d", i); } }' | gcc -Wshadow -xc -c -
<stdin>: In function 'f':
<stdin>:2:53: warning: declaration of 'i' shadows a previous local [-Wshadow]
<stdin>:2:20: note: shadowed declaration is here