内部循环中创建的变量在C

时间:2016-02-12 06:35:36

标签: c variables for-loop stack automatic-storage

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

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

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

PS - 当我在我的个人计算机上运行相同的代码时,我得到了预期的输出。但在生产方面却有所不同。

2 个答案:

答案 0 :(得分:7)

首先,为了清楚自动局部变量的存储持续时间,让我引用C11标准,章节§6.2.4,(强调我的

  

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

和,

  

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

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

在您的代码中,

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

您正在尝试使用具有不确定值的单位化自动局部变量j。它调用undefined behavior

根据C11,章节§6.7.9

  

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

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

  

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

一旦你的代码命中UB,无论如何都无法证明输出是合理的。

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

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

答案 1 :(得分: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的最后一个值,可通过new访问,没有初始化的。