4

我在我们的产品中有类似于以下的代码。据我说,输出是'0 1 2 3'。但是类似代码的输出是'1 1 1 1'。

for(i = 0 ;i < 5;i++){
    int j;
    if(i)
        printf("%d ",j);
    j = i;
}

我的理解是 j 在整个“for”循环期间仅在堆栈上分配一次,并且在迭代期间使用相同的值。另外,如果我将 j 的声明移到 for 循环之外,我会得到预期的结果。我在这里想念什么?

PS - 当我在我的个人机器上运行相同的代码时,我得到了预期的输出。但是在生产上就不同了。

4

2 回答 2

7

首先,为了明确关于自动局部变量的存储持续时间,让我引用C11标准,第 §6.2.4 章,(强调我的)

其标识符被声明为没有链接且没有存储类说明符的对象static具有自动存储持续时间,[...]

和,

对于这样一个没有可变长度数组类型的对象,它的生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。(进入封闭的块或调用函数会暂停,但不会结束当前块的执行。)如果递归地进入块,则每次都会创建一个新的对象实例。对象的初始值是不确定的。

因此,在您的代码中,每次迭代都会获得一个j. 什么都没有保留。

在您的代码中,

    int j;   //not initialized
    if(i)
        printf("%d ",j);  //this one here

您正在尝试使用j具有不确定值的未初始​​化的自动局部变量。它调用未定义的行为

根据C11第 §6.7.9 章

如果具有自动存储持续时间的对象未显式初始化,则其值是不确定的

和相关的,对于 UB,附件 §J.2

具有自动存储持续时间的对象的值在不确定时使用。

一旦您的代码到达 UB,无论如何,输出都无法证明是合理的。

OTOH,当您j在循环外声明时,它具有函数范围。然后,与上述情况不同,循环的所有迭代都只有一个实例。j

根据执行流程,第一次i为 0,if将评估为 false,printf()将被跳过并被j初始化。然后,在下一次迭代中,当你点击 , 时printf()j它会被初始化,此后一切都很好。

于 2016-02-12T06:37:15.743 回答
0

为了清楚起见,我认为 for 循环将在引擎盖下转换为:

i = 0;
LoopStart:
if(!(i<5)){ goto LoopEnd;}

{
    int j;
    if(i)
        printf("%d ",j);
    j = i;
}

i++;
goto LoopStart;
LoopEnd:

实际的实现会有所不同,但这有助于强调这一点:循环的每次迭代都会进入和退出块,这意味着在这个例子中,块中的每个 auto 被创建销毁 5 次。正如其他人提到的,这意味着您每次在 printf 中都使用未初始化的 j。

至于为什么代码可能在某些平台/编译器上工作。这可能是因为 j 每次都分配了相同的堆栈地址,并且编译器在创建或销毁 j 时不会清除堆栈,所以恰好分配给旧的死 j 的最后一个值可以通过新的访问,未初始化的。

于 2016-02-12T07:08:58.750 回答