2

考虑 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作为输出。-O1但是由于某种原因,GCC 4.7 在启用优化(和更高版本)的情况下不会这样做。而是将随机数据放在字符数组的第一个字节中,变为:

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

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

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

4

4 回答 4

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;
    }
}
于 2012-07-25T13:35:14.747 回答
2

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

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

于 2012-07-25T13:36:01.360 回答
0

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

于 2012-07-25T13:35:00.933 回答
-1

每次迭代都会为数组分配内存,并且只有在最后一次迭代中获得的值。

于 2012-07-25T13:58:02.247 回答