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

时间:2012-07-25 13:32:04

标签: c loops scope variable-declaration

考虑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' |

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

正确的行为应该是什么? 我知道将x的声明移出循环“修复”它,但这并没有说明此代码段的行为。现实世界的问题是bug in Netfilter

4 个答案:

答案 0 :(得分:4)

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

  

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

关闭优化后,阵列会自动存储在同一位置,因此您的程序运气良好;但是,这绝不是保证。

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

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:-1)

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