假设我有一个for
循环,其中有一个局部变量:
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
的内存是否在外循环的第一次迭代结束时在主内存中释放,并在外循环的后续迭代开始后再次分配?
在我看来,循环的范围和方法的范围是不同的。当函数完全执行后,局部变量将被释放,内存将返回给操作系统。
但是对于循环,当作用域在每次迭代结束时结束时,局部变量不会被释放,因为它们是在不久的将来(后续迭代)使用的。只有在循环结束它的执行之后,局部变量才应该从主内存中释放。
如果错了请指正。
C++的行为是以抽象机器的形式定义的。在这台机器中,具有自动存储持续时间的对象的存储器保证从每次进入块的开始(块中代码的执行开始)一直存在到块的执行结束。与int p[10]
关联的块是由{
在for(int i=0;i<10;i++)
之后立即创建的块。此块的执行在for
循环的每次迭代中开始和结束。
尽管编译器很可能通过分配堆栈空间一次来实现这个循环(可能是在它所在的函数启动时,而不是到达for
循环时),因此用于实现int p[10]
的内存仍然分配给整个循环,而不是每次迭代,但程序的行为是根据抽象机器定义的。
这意味着尽管堆栈空间仍然保留,但编译器的优化不需要这样做。优化可能导致代码在迭代中“忘记”p
元素中的值。
例如,如果我们使用Apple Clang 11.0在macOS 10.14.6上使用-o3-std=C++17
执行此代码:
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
,但直到完成外部循环。int p[10];i=0时
与int p[10]不同;i=1时
等,因此i=1时,先前的p
已经消失。任何其他优化(或假设)都依赖于编译器、体系结构等。
要说明的是,p
位于堆栈上,它的作用域将决定何时释放内存(与堆上的变量不同)。
可能有点吹毛求疵,但使用现代C++容器,如std::array
或std::vector
,而不是原始的普通C数组。
要改进资源管理,请查看RAII。