提问者:小点点

在主存循环中分配局部变量


假设我有一个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的内存是否在外循环的第一次迭代结束时在主内存中释放,并在外循环的后续迭代开始后再次分配?

在我看来,循环的范围和方法的范围是不同的。当函数完全执行后,局部变量将被释放,内存将返回给操作系统。

但是对于循环,当作用域在每次迭代结束时结束时,局部变量不会被释放,因为它们是在不久的将来(后续迭代)使用的。只有在循环结束它的执行之后,局部变量才应该从主内存中释放。

如果错了请指正。


共2个答案

匿名用户

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::arraystd::vector,而不是原始的普通C数组。

要改进资源管理,请查看RAII。