考虑C中的循环,它在循环体中声明一个字符数组 . 在每次迭代时,修改数组的字符,直到到达结尾 . 最后,打印变量 . 描述将扩展到下一个代码:
#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;
许多人可能期望 0123
作为输出 . 但由于某些原因,GCC 4.7在启用优化( -O1
和更高版本)进行编译时不会这样做 . 它将随机数据放在字符数组的第一个字节中,变为:
| 0 | 1 | 2 | 3 | 4 |
| RANDOM | '3' | '\0' |
我认为从语言的角度来看这是逻辑行为:自动变量在块终止后消失,因此应该预期上述“随机”行为 .
什么应该是正确的行为? 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 . 现实世界的问题是bug in Netfilter .
4 回答
由于数组是在循环体的范围内声明的,因此您可以将其视为在每个循环迭代的自动存储区域中分配的新数组 . 除了在当前迭代期间为其分配的索引处的字符外,该单元化数组的内容是未定义的,因此您看到的是不确定的值:
关闭优化后,阵列会自动存储在同一位置,因此您的程序运气良好;但是,这绝不是保证 .
将数组移动到循环外部,如下所示:
这是块范围的问题 . 循环体是一个块,由
{}
表示,它在每次循环迭代中输入并保留 . 每次输入块时,您(概念上)都会获得具有未指定内容的新x
.由于您只在调用
printf
的迭代中设置x[3] = '3'
和x[4] = '\0'
,因此只有这两个数组成员具有明确定义的内容 . 其余可能包含任何内容,包括'\0'
,因此printf
可以输出任何长度最多为2的字符串,可选地后跟任何其他字符和3
.这将按预期输出(随机字符数组),因为自动变量在声明终止后的范围之后被清除 . 要修复,请在
for
循环范围之外移动x
.数组将为每次迭代分配内存,并且它将仅具有在上一次迭代中获得的值 .