2015-09-25 62 views
0

壳体1:内存分配在循环变量声明

int main() 
{ 
    int T=5; 
    while(T--){ 
     int a; 
     cout<<&a<<"\n"; 
    } 
} 

它打印相同的地址的5倍。

我想它应该打印5个不同的地址。

的情况下2:

int main() 
{ 
    int T=5; 
    while(T--){ 
     int* a=new int; 
     cout<<a<<"\n"; 
    } 
} 

印刷5个不同的地址

我的问题是:
为什么一点儿也不新的内存分配每一个变量声明在第一种情况下遇到的时间呢?
和第一例与第二例的区别。

回答

3

在第一种情况下,a位于堆栈上。基本上,a在每次迭代中得到“构建”(更好的措词可能是“分配空间”),然后发布。因此,在每次迭代之后,先前分配给a的空间再次释放,并且新的a在下一次迭代中获取该空间。这就是地址相同的原因。

在第二种情况下,您在堆上分配内存并(另外)不要再释放它。所以内存不能在下一次迭代中重新分配。

+2

而在第二种情况下,分配的内存被泄漏,所以它*有*每次得到一个新地址 –

+0

@BoPersson的权利,第二种情况是在我回答后添加的。将其包含在答案中。 – anderas

0

这取决于编译器。由于该变量是在最内层范围内声明的,因此编译器认为可以为该变量重新使用相同的位置。当然,它可以位于不同的地址。

+2

理论上它可能取决于编译器。但实际上它几乎肯定不能。本地分配在一个循环内部,所以编译器很难重复使用相同的位置,所有这些困难都可以达到零的目的。 – JSF

+0

@JSF是的。我的意思是编译器不必重复使用该地址,尽管它大部分时间都会重用它。 – mkdong

1

理论上,每次变量进入作用域时,堆栈上的绝对位置都会被分配,每次超出作用域时都会释放它。然后分配堆栈的LIFO特性确保每次都分配相同的位置。

但实际上,只要这样做是实用的(在这种情况下是平凡的),编译器在编译时分配堆栈上的相对位置。通过预先分配的相对位置,进入函数的简单行为可以有效地分配所有局部变量的所有实例。像这样的循环中的局部对象将针对每个实例被构造和/或初始化,但是对于所有实例预先分配一次。因此,地址与堆栈的LIFO特性相比更为根本的原因是相同的。他们是一样的,因为分配只做了一次。

如果您的C++编译器支持通用的C99功能,则可以构建可区分上述两种情况的测试。事情大致是这样:

for (int i=0; i<2; ++i) { 
    int unpredictable[ f(i) ]; 
    for (int j=0; j<2; ++j) { 
     int T=5; 
     // does the location of T vary as i changes ?? 
     int U[ f(j) ]; // I'm pretty sure the location of U varies 
}} 

我们希望f的值(0)和f(1)很容易在运行时间,但很难优化看到在编译时。如果在此模块中声明了f,但在另一个模块中定义了f,则这是最稳健的。

通过阻止编译器在编译时完成所有的分配,我们可以阻止它在编译时进行一些简单的分配,或者仍然可以在编译时和运行时分配它们分配仅在需要时使用。