Consider a loop in C which declares a character array in the loop's body. At each iteration, a character of array is modified until the end is reached. At the end, the variable is printed. The description would expand to the next code:
#include <stdio.h>
int main(void) {
int i = 0;
for (;;) {/* same as: while(1) { */
char x[5];
x[i] = '0' + i;
if (++i == 4) {
x[i] = '\0'; /* terminate string with null byte */
printf("%s\n", x);
break;
}
}
return 0;
Many may expect 0123
as output. But for some reason GCC 4.7 does not do that when compiling with optimization enabled (-O1
and higher). It instead puts random data in the first bytes of the character array, which becomes:
| 0 | 1 | 2 | 3 | 4 |
| RANDOM | '3' | '\0' |
I think that this is logical behaviour from the language point of view: automatic variables are gone after a block is terminated, so the above "random" behaviour should be expected.
What should be the correct behaviour? I know that moving the declaration of x
outside the loop "fixes" it, but that does not say anything about the behaviour of this snippet. The real-world problem is a bug in Netfilter.
Since the array is declared inside the scope of loop's body, you can think of it as of a new array being allocated in the automatic storage area for each loop iteration. The content of that unititialized array is undefined, except for the character at the index to which you have assigned during the current iteration, so what you see there is indeterminate value:
C99 standard, section 6.7.8: If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate
When optimization is turned off, the array lands in the same spot in the automatic storage, so your program gets lucky; however, this is by no means guaranteed.
Move the array to outside the loop, like this:
int i = 0;
char x[5];
for (;;) {
x[i] = '0' + i;
if (++i == 4) {
x[i] = '\0'; /* terminate string with null byte */
printf("%s\n", x);
break;
}
}
This is a matter of block scope. The loop body is a block, denoted by {}
, which is entered and left in every loop iteration. Every time the block is entered, you get (conceptually) a new x
with unspecified contents.
Since you've only set x[3] = '3'
and x[4] = '\0'
in the iteration where printf
is called, only those two array members have well-defined content. The rest may contain anything, including '\0'
, so printf
may output any string of length at most 2, optionally followed by any other character and 3
.
This would output as expected (a random character array), as an auto-variable is cleaned up after the scope in which it is declared terminates. To fix, move x
outside of the scope of the for
loop.
The array will be allocated memory for each iteration and it will have the value gained in the last iteration only.