我的产品代码类似于以下内容。据我说,输出是'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 - 当我在我的个人计算机上运行相同的代码时,我得到了预期的输出。但在生产方面却有所不同。
答案 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访问,没有初始化的。