在循环中创建的变量在 C 中的迭代期间更改值
Variable created inside loop changes value during iterations in C
我的产品中有类似下面的代码。根据我的说法,输出是“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 - 当我在我的个人计算机上 运行 相同的代码时,我得到了预期的输出。但是在生产上就不一样了。
首先,为了弄清楚自动局部变量的存储持续时间,让我引用 C11
标准,第 6.2.4 章,(强调我的 )
An object whose identifier is declared with no linkage and without the storage-class
specifier static
has automatic storage duration, [...]
并且,
For such an object that does not have a variable length array type, its lifetime extends
from entry into the block with which it is associated until execution of that block ends in
any way. (Entering an enclosed block or calling a function suspends, but does not end,
execution of the current block.) If the block is entered recursively, a new instance of the
object is created each time. The initial value of the object is indeterminate.
因此,在您的代码中,每次迭代都会获得 j
的 新实例 。什么都没有保留。
在您的代码中,
int j; //not initialized
if(i)
printf("%d ",j); //this one here
您正在尝试使用具有不确定值的单元化自动局部变量 j
。它调用 undefined behavior.
根据 C11
,章节 §6.7.9
If an object that has automatic storage duration is not initialized explicitly, its value is
indeterminate
及相关,对于 UB,附件 §J.2
The value of an object with automatic storage duration is used while it is
indeterminate.
一旦你的代码命中UB,无论如何输出都无法证明。
OTOH,当您在循环外声明 j
时,它具有函数作用域。然后,与上述情况不同,对于循环的所有迭代,将只有 one 个 j
实例。
根据执行流程,第一次,i
为 0,if
将计算为 false,printf()
将被跳过,j
将被初始化。然后,在下一次迭代中,当您点击 printf()
时,j
被初始化,此后一切正常。
为清楚起见,我认为 for 循环将在幕后转换为类似以下内容:
i = 0;
LoopStart:
if(!(i<5)){ goto LoopEnd;}
{
int j;
if(i)
printf("%d ",j);
j = i;
}
i++;
goto LoopStart;
LoopEnd:
实际的实现会有所不同,但这有助于强调这一点:
对于循环的每次迭代,块进入 和 退出,这意味着块中的每个汽车在本示例中被创建 和 销毁 5 次。
正如其他人提到的,这意味着您每次在 printf 中都使用未初始化的 j。
至于为什么代码 可能 在某些平台/编译器上工作。这可能是因为每次都为 j 分配了相同的堆栈地址,并且编译器在创建或销毁 j 时不会清除堆栈,所以恰好分配给旧的死 j 的最后一个值可以通过新的访问,未初始化的。
我的产品中有类似下面的代码。根据我的说法,输出是“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 - 当我在我的个人计算机上 运行 相同的代码时,我得到了预期的输出。但是在生产上就不一样了。
首先,为了弄清楚自动局部变量的存储持续时间,让我引用 C11
标准,第 6.2.4 章,(强调我的 )
An object whose identifier is declared with no linkage and without the storage-class specifier
static
has automatic storage duration, [...]
并且,
For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate.
因此,在您的代码中,每次迭代都会获得 j
的 新实例 。什么都没有保留。
在您的代码中,
int j; //not initialized
if(i)
printf("%d ",j); //this one here
您正在尝试使用具有不确定值的单元化自动局部变量 j
。它调用 undefined behavior.
根据 C11
,章节 §6.7.9
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate
及相关,对于 UB,附件 §J.2
The value of an object with automatic storage duration is used while it is indeterminate.
一旦你的代码命中UB,无论如何输出都无法证明。
OTOH,当您在循环外声明 j
时,它具有函数作用域。然后,与上述情况不同,对于循环的所有迭代,将只有 one 个 j
实例。
根据执行流程,第一次,i
为 0,if
将计算为 false,printf()
将被跳过,j
将被初始化。然后,在下一次迭代中,当您点击 printf()
时,j
被初始化,此后一切正常。
为清楚起见,我认为 for 循环将在幕后转换为类似以下内容:
i = 0;
LoopStart:
if(!(i<5)){ goto LoopEnd;}
{
int j;
if(i)
printf("%d ",j);
j = i;
}
i++;
goto LoopStart;
LoopEnd:
实际的实现会有所不同,但这有助于强调这一点: 对于循环的每次迭代,块进入 和 退出,这意味着块中的每个汽车在本示例中被创建 和 销毁 5 次。 正如其他人提到的,这意味着您每次在 printf 中都使用未初始化的 j。
至于为什么代码 可能 在某些平台/编译器上工作。这可能是因为每次都为 j 分配了相同的堆栈地址,并且编译器在创建或销毁 j 时不会清除堆栈,所以恰好分配给旧的死 j 的最后一个值可以通过新的访问,未初始化的。