在迭代期间保留在循环体中声明的变量吗?

考虑C中的循环,它在循环体中声明一个字符数组。 在每次迭代时,都会修改数组的字符,直到到达结尾。 最后,打印变量。 描述将扩展到下一个代码:

#include  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; 

许多人可能期望0123作为输出。 但由于某些原因,GCC 4.7在启用优化( -O1和更高版本)进行编译时不会这样做。 它将随机数据放在字符数组的第一个字节中,这将成为:

 | 0 | 1 | 2 | 3 | 4 | | RANDOM | '3' | '\0' | 

从语言的角度来看,我认为这是逻辑行为:自动变量在块终止后消失,因此应该预期上述“随机”行为。

什么应该是正确的行为? 我知道在循环外部移动x的声明“修复”它,但这并没有说明这个片段的行为 。 现实问题是Netfilter中的一个错误 。

由于数组是在循环体的范围内声明的,因此您可以将其视为在每个循环迭代的自动存储区域中分配的新数组。 该单元化数组的内容是未定义的,除了您在当前迭代期间指定的索引处的字符,因此您看到的是不确定的值:

C99标准,第6.7.8节 :如果没有显式初始化具有自动存储持续时间的对象,则其值不确定

当关闭优化时,arrays落在自动存储器中的相同位置,因此您的程序很幸运; 但是,这绝不是保证。

将数组移动到循环外部,如下所示:

 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; } } 

这是块范围的问题。 循环体是一个块,用{}表示,在每次循环迭代中输入并保留。 每次输入块时,您(从概念上)都会得到一个带有未指定内容的新x

由于在调用printf的迭代中只设置了x[3] = '3'x[4] = '\0' ,因此只有这两个数组成员具有明确定义的内容。 其余的可能包含任何内容,包括'\0' ,因此printf可以输出任何长度最多为2的字符串,可选地后跟任何其他字符和3

这将按预期输出(随机字符数组),因为自动变量在声明终止后的范围之后被清除。 要修复,请将x移到for循环范围之外。

数组将为每次迭代分配内存,并且它将仅在最后一次迭代中获得值。