问题描述
for(int i=0;i<10;i++){ // Outer loop
int p[10]; // Local variable
for(int j=1;j<10;j++){ // Inner loop
p[j] = p[j-1]+1;
}
}
当所有编译器优化都关闭时,很明显,对于外循环的第一次迭代,数组p
将被分配到主内存中。
但是,分配给 p
的内存是否在 外 循环的第一 迭代结束时在主内存中释放并分配在外循环的后续迭代开始后再次?
在我看来,循环的范围和方法的范围是不同的。函数执行完毕后,局部变量被释放,内存返回给操作系统。
但是 for 循环,当作用域在每次迭代结束时结束时,局部变量不会因为它们在不久的将来(后续迭代)中使用而被释放。只有在循环结束后才执行,局部变量才应该从主内存中释放。
如有错误请指正。
解决方法
C++ 的行为是根据抽象机器定义的。在这台机器中,从每个进入块的开始(块中代码的执行开始)到块的执行结束,具有自动存储持续时间的对象的内存都被保证存在。与 int p[10]
关联的块是由 {
紧跟在 for(int I=0;i<10;i++)
之后创建的块。此块的执行在 for
循环的每次迭代中开始和结束。
虽然编译器很可能通过分配一次堆栈空间来实现这个循环(可能是在它所在的函数启动时,而不仅仅是在到达 for
循环时),因此用于实现的内存int p[10]
保持分配给整个循环,而不仅仅是每次迭代,程序的行为是根据抽象机定义的。
这意味着虽然堆栈空间仍然保留,但编译器的优化不需要以这种方式运行。优化可能会导致代码在每次迭代中“忘记”p
元素中的值。
例如,如果我们在 macOS 10.14.6 和 -O3 -std=c++17
上使用 Apple Clang 11.0 执行此代码:
int p[10];
for (int i = 0; i < 10; i++)
{
if (i == 0)
p[0] = 0;
else
p[0] = p[0]+1;
for (int j = 1; j < 10; j++)
{
p[j] = p[j-1] + 1;
}
if (i == 9)
std::cout << p[0] << '\n';
}
则输出为“9”,因为 p[0]
在第一次迭代中设置为 0,并在以后的每次迭代中递增。但是,我们将 int p[10];
移动到循环内,输出为“0”,因为编译器优化没有在迭代之间保留 p[0]
的值。
p
的作用域是外循环,因此每次迭代时它都会在堆栈上被销毁和重新创建。唯一未被销毁的变量是 i
,但直到外循环完成。当 i=0 时的 int p[10];
不同于当 i=1 时的 int p[10];
等等,所以当 i=1 时,之前的 p
已经消失了。任何其他优化(或假设)都依赖于编译器、架构等。
澄清一下,p
存在于堆栈中,其作用域将决定何时释放内存(与堆上的变量不同)。
可能有点吹毛求疵,但请使用现代 C++ 容器,例如 std::array
或 std::vector
,而不是原始的普通 C 数组。
要改进资源管理,请查看 RAII。