在循环内创建的变量在 C 中的迭代期间更改值

Posted

技术标签:

【中文标题】在循环内创建的变量在 C 中的迭代期间更改值【英文标题】:Variable created inside loop changes value during iterations in C 【发布时间】:2016-05-23 05:30:00 【问题描述】:

我的产品中有类似以下的代码。据我说,输出是'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 - 当我在我的个人机器上运行相同的代码时,我得到了预期的输出。但在生产上就不同了。

【问题讨论】:

【参考方案1】:

首先,为了明确关于自动局部变量的存储持续时间,让我引用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 被初始化,此后一切都很好。

【讨论】:

另外,我认为如果i=0 ,if 声明不起作用。 不会只有UB第一次通过循环吗?在第一个循环之后,分配了 J。 @RobertHarvey Nopes,每个j 都是独一无二的。请参阅§6.2.4/6 那么OP关于所有循环迭代中使用相同J的假设一定是无效的。我希望每次都会创建一个新的 J,所以我对 OP 的断言有点惊讶,但我不是 C 专家。 @Holsety 不,它重复使用相同的空间。从概念上讲,当执行到达int j; 时分配变量,并在到达以下 时释放。编译器将通过将特定的堆栈位置分配给 j 来优化它【参考方案2】:

为了清楚起见,我认为 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 的最后一个值可以通过新的访问,未初始化的。

【讨论】:

只是吹毛求疵:当goto 可以明显避免时,我不会提倡使用它。 天哪,我不是 :P 只是用它来说明循环确实退出循环块并因此破坏块的本地汽车 每次循环迭代

以上是关于在循环内创建的变量在 C 中的迭代期间更改值的主要内容,如果未能解决你的问题,请参考以下文章

丰富的数据表不能将迭代变量用于嵌套循环

如何在每次创建 msi 期间动态更改 wix 中的产品版本

Pandas - 迭代中的重复行

函数或循环从 R 中的数据更改名称,迭代多次

是否为每次迭代创建了 for 循环内的引用变量声明? [复制]

node.js 回调获取变量的意外值