【问题标题】:When does the space occupied by a variable get deallocated in c++?变量占用的空间何时在 C++ 中被释放?
【发布时间】:2009-07-07 12:04:03
【问题描述】:

控制权从函数返回后,变量占用的空间不会被释放吗??

我以为它被释放了。

在这里,我编写了一个函数,即使在从函数 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;
}

【问题讨论】:

    标签: c++ scope


    【解决方案1】:

    函数返回后,局部变量占用的内存内容是未定义的,但实际上它会保持不变,直到有东西主动改变它。

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

    【讨论】:

    • 仅供参考 - “失败”是指代码仍然可以工作,但它会给你带来不可预知的结果。这通常比传统意义上的失败(分段错误等)更糟糕
    • 或者,如果在某些操作系统上,中断关闭或您收到信号。它可能是那些非常难以追踪的间歇性错误之一。
    • 或者如果返回导致堆栈收缩到足以让操作系统卸载页面。然后尝试访问该页面可能会导致段错误。
    【解决方案2】:

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

    int min[Sum+1];
    

    但一般来说,您的程序会表现出未定义的行为。这意味着任何事情都可能发生 - 它甚至可能看起来有效。

    【讨论】:

    • 真的非法C++吗?我不太懂 C++,但是这行代码看起来确实像有效的 C...
    • 它是有效的 C99。如果 Sum 是一个常量,那将是有效的 C++,但它是一个变量。
    • 哦,不错的发现。我没有发现。
    • +1 表示“任何事情都可能发生——它甚至可能会起作用。”
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      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;
      

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

      【讨论】:

      • 不是 OP 所要求的,但这确实是正确/可靠的方法。
      • 不,正确可靠的方法是使用 std::vector。
      • std::vector 将为您调用分配和释放秘密,我刚刚指出,您不能从函数调用中返回局部变量,因为堆栈和堆变量之间存在差异。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        您用于数组的变量分配在堆栈上,堆栈对程序完全可用 - 空间没有被阻塞或隐藏。

        它被释放的意义在于它可以在以后被其他函数调用重用,以及为在那里分配的变量调用析构函数。整数的析构函数是微不足道的,不做任何事情。这就是为什么您可以访问它,并且可能发生数据尚未被覆盖而您可以读取它的情况。

        【讨论】:

        • 我在向下滚动提到堆栈的页面时找到的第一个答案。
        【解决方案7】:

        答案是语言标准允许的内容与实际可行的内容(在这种情况下)之间存在差异,这取决于具体实现的工作方式。

        标准说内存不再使用,所以不能引用。

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

        在您的情况下,数组数据还没有被其他任何东西覆盖,因此您的代码似乎可以工作。调用另一个函数,数据被覆盖。

        【讨论】:

          【解决方案8】:

          如果空间被释放,它如何打印正确的答案??

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

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

          不能保证它不会被重复使用;而且你甚至可以在它被释放后读取它的事实也不能保证:所以不要这样做。

          【讨论】:

            【解决方案9】:

            这可能有效,也可能无效,行为未定义,这样做绝对是错误的。大多数编译器也会给出编译器警告,例如 GCC:

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

            【讨论】:

              【解决方案10】:

              记忆就像永远不会变硬的粘土。分配内存就像从陶罐中取出一些粘土。也许你会做一只猫和一个芝士汉堡。完成后,您通过将人物放回锅中来释放粘土,但只是放入锅中不会使它们变形:如果您或其他人看着锅,他们会继续观察您的猫和芝士汉堡坐在粘土堆的顶部,直到其他人出现并将它们变成其他东西。

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

              【讨论】:

                【解决方案11】:

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

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

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

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

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

                这将是 (Sum + 3) * 4 字节/整数。假设 Sum = 2,这给了我们另外 20 个字节,所以堆栈指针增加 32 个字节到 0x1020。 (所有 main 的本地变量都低于堆栈上的 0x1000。) min 将指向 0x100c。当 CoinDenom() 返回时,堆栈指针递减以“释放”该内存,但除非调用另一个函数以在该内存中分配它自己的局部变量,否则不会发生任何事情来更改该内存中存储的内容。

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-20
                  • 2016-01-09
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-11-25
                  相关资源
                  最近更新 更多