变量占用的空间何时在c ++中解除分配?

时间:2009-07-07 12:04:03

标签: c++ scope

一旦从函数返回控件,变量占用的空间是否会被释放?

我以为它被解除了分配。

这里我编写了一个函数,即使在从函数CoinDenom返回数组的本地引用之后也能正常工作,使用它来打印命名总和所需的最小硬币数的结果。 如果空间被解除分配,它如何能够打印出正确的答案?

int* CoinDenom(int CoinVal[],int NumCoins,int Sum) {
  int min[Sum+1];
  int i,j;
  min[0]=0;
  for(i=1;i<=Sum;i++) {
    min[i]=INT_MAX;
  }

  for(i=1;i<=Sum;i++) { 

    for(j=0;j< NumCoins;j++) {

      if(CoinVal[j]<=i && min[i-CoinVal[j]]+1<min[i]) {
        min[i]=min[i-CoinVal[j]]+1;
      }
    }
  }
  return min; //returning address of a local array
}

int main() {

  int Coins[50],Num,Sum,*min;
  cout<<"Enter Sum:";
  cin>>Sum;
  cout<<"Enter Number of coins :";
  cin>>Num;
  cout<<"Enter Values";
  for(int i=0;i<Num;i++) {
    cin>>Coins[i];
  }

  min=CoinDenom(Coins,Num,Sum);
  cout<<"Min Coins required are:"<< min[Sum];
  return 0;
}

11 个答案:

答案 0 :(得分:18)

函数返回后,局部变量占用的内存内容是未定义的,但实际上它会保持不变,直到某些内容发生变化。

如果您更改代码以在填充内存然后使用它之间做一些重要的工作,您将看到它失败。

答案 1 :(得分:7)

您发布的内容不是C ++代码 - 以下内容在C ++中是非法的:

int min[Sum+1];

但总的来说,你的程序表现出不确定的行为。这意味着任何事情都可能发生 - 它甚至可能起作用。

答案 2 :(得分:6)

当函数返回时,空格被“释放” - 但这并不意味着数据不在内存中。数据仍将在堆栈中,直到其他一些函数覆盖它为止。这就是为什么这些类型的错误是如此棘手 - 有时它会工作得很好(直到突然它没有)

答案 3 :(得分:2)

您需要在堆上为返回变量分配内存。

int* CoinDenom(int CoinVal[],int NumCoins,int Sum) {
  int *min= new int[Sum+1];
  int i,j;
  min[0]=0;
  for(i=1;i<=Sum;i++) {
    min[i]=INT_MAX;
  }

  for(i=1;i<=Sum;i++) { 

    for(j=0;j< NumCoins;j++) {

      if(CoinVal[j]<=i && min[i-CoinVal[j]]+1<min[i]) {
        min[i]=min[i-CoinVal[j]]+1;
      }
    }
  }
  return min; //returning address of a local array
}




  min=CoinDenom(Coins,Num,Sum);
  cout<<"Min Coins required are:"<< min[Sum];
  delete[] min;
  return 0;

在您的情况下,您只能看到正确的值,因为没有人试图更改它。总的来说,这是不可预测的情况。

答案 4 :(得分:2)

用于数组的变量在堆栈上分配,堆栈完全可用于程序 - 空间不会被阻塞或以其他方式隐藏。

它被解除分配,因为它可以在以后重用于其他函数调用,并且在某种意义上,析构函数会被调用在那里分配的变量。整数的析构函数是微不足道的,不做任何事情。这就是为什么你可以访问它,并且可能发生数据尚未被覆盖而且你可以阅读它。

答案 5 :(得分:2)

该数组位于堆栈上,在大多数实现中,该数组是预先分配的连续内存块。你有一个指向堆栈顶部的堆栈指针,增长堆栈意味着只需沿着它移动指针。

当函数返回时,堆栈指针被设置回来,但是内存仍然存在,如果你有一个指向它的指针,你可以访问它,但这样做是不合法的 - 虽然没有什么可以阻止你。下次堆栈深度在数组所在区域上运行时,数组旧空间中的内存值将发生变化。

答案 6 :(得分:1)

答案是语言标准允许的内容与结果之间存在差异(在这种情况下),因为具体实现的工作原理。

标准规定不再使用内存,因此不得引用。

实际上,堆栈上的局部变量。在应用程序终止之前,堆栈内存不会被释放,这意味着您永远不会因为写入堆栈内存而遇到访问冲突/分段错误。但是你仍然违反了C ++的规则,它并不总是有效。编译器可以随时覆盖它。

在您的情况下,阵列数据尚未被其他任何内容覆盖,因此您的代码似乎可以正常工作。调用另一个函数,数据被覆盖。

答案 7 :(得分:1)

  

如果空间被解除分配,它如何打印正确答案?

当内存被释放时,它仍然存在,但它可能会被重用于其他内容。

在您的示例中,数组已被释放但其内存尚未被重用,因此其内容尚未被其他值覆盖,这就是为什么您仍然可以使用您编写的值

不能保证它不会被重复使用的事实;并且在取消分配之后你甚至可以从中读取它的事实也不能得到保证:所以不要这样做。

答案 8 :(得分:0)

这可能会或可能不会起作用,行为是未定义的,这样做肯定是错误的。大多数编译器也会发出编译器警告,例如GCC:

test.cpp:8: warning: address of local variable `min' returned

答案 9 :(得分:0)

记忆就像粘土一样永不变硬。分配记忆就像从粘土罐中取出一些粘土。也许你做了一只猫和一个芝士汉堡。当你完成后,你通过将你的数字放回锅中来释放粘土,但只是放入锅中不会使它们失去形状:如果你或其他人看着锅,他们会继续观察你的猫和芝士汉堡坐在粘土堆栈的顶部,直到有其他人进来并使他们成为别的东西。

存储芯片中的NAND门是粘土,保持NAND门的层是粘土罐,代表变量值的特定电压是你的雕塑。这些电压不会因为你的程序将它们从它关心的事物列表中删除而改变。

答案 10 :(得分:0)

您需要了解堆栈。添加此功能,

void f() 
{ 
    int a[5000];
    memset( a, 0, sizeof(a) );
}

然后在调用CoinDenom()之后立即调用它,但在写入cout之前。你会发现它不再适用。

您的本地变量存储在堆栈中。 CoinDenom()返回指向堆栈的内存地址。非常简化并省略了许多细节,比如在你调用CoinDenom之前,堆栈指针指向0x1000。 int *(Coins)被推入堆栈。这变成了CoinVal []。然后是一个int,Num成为NumCoins。然后是另一个int,Sum变为Sum。那是4个字节/ int的3个整数。然后是局部变量的空间:

int min[Sum+1];
int i,j;

将是(Sum + 3)* 4 bytes / int。 Say Sum = 2,总共给出了20个字节,因此堆栈指针增加了32个字节到0x1020。 (所有主要的本地人在堆栈上都低于0x1000。)min将指向0x100c。当CoinDenom()返回时,堆栈指针递减“释放”该内存,但除非调用另一个函数使其自己的本地分配在该内存中,否则不会发生任何改变存储在该内存中的内容。

有关如何管理堆栈的更多详细信息,请参阅http://en.wikipedia.org/wiki/Calling_convention