【问题标题】:Automatic heap cleanup during stack destruction堆栈销毁期间的自动堆清理
【发布时间】:2011-04-14 18:42:30
【问题描述】:
int* f()
{
    int *p = new int[10];
    return p;
}

int main()
{
    int *p = f();
    //using p;
    return 0;
}

当函数返回它的值时,在堆栈销毁期间,某些编译器(当我被告知时暗示了常见的编译器,如 VS 或 gcc)是否可以尝试自动释放由本地指针(如本例中的 p)指向的内存?即使不是,我是否能够正常删除 [] 在 main 中分配的内存?问题似乎是关于确切数组大小的信息在这一点上丢失了。另外,在 malloc 和 free 的情况下答案会改变吗?

谢谢。

【问题讨论】:

    标签: c++ c memory allocation


    【解决方案1】:

    只有本地变量被销毁释放。

    在您的情况下,p 被“破坏”(已发布),但 p 所指的是不是“被破坏”(使用 delete[] 发布)。

    是的,您可以并且应该/必须在您的主服务器上使用delete[]。但这并不意味着在C++ 中使用原始指针。您可能会觉得这本电子书很有趣:Link-Alf-Book

    如果您想delete 当函数“结束”(超出范围)时局部变量指向的内容,请使用 std::auto_ptr()(但仅适用于非数组变量,不适用于需要 delete[] 的变量) )

    另外,如果万一,答案会改变吗? malloc 和 free 的?

    不,但您应该确保不要混用 free()/new/delete/malloc()。这同样适用于new/delete[]new[]/delete

    【讨论】:

      【解决方案2】:

      不,它们不会 freedelete 您的指针指向的内容。它们只会释放指针本身占用的几个字节。我相信,调用 freedelete 的编译器会违反语言标准。

      如果您是指向内存的指针,即来自f() 的结果,您将只能在main 中访问delete[] 内存。您不需要跟踪分配的大小; newmalloc 在幕后为你做这件事。

      如果您希望在函数返回时清理内存,请使用智能指针,例如 boost::scoped_ptrboost::scoped_array(均来自 Boost 库集合)、std::auto_ptr(在当前 C++ 标准中,但大约将被弃用)或std::unique_ptr(在即将发布的标准中)。

      在 C 中,不可能创建智能指针。

      【讨论】:

        【解决方案3】:

        当函数返回它的值时,在堆栈销毁期间,某些编译器(当我被告知时暗示了常见的编译器,如 VS 或 gcc)是否可以尝试自动释放由本地指针(如本例中的 p)指向的内存?

        简短回答:否

        长答案:

        如果您正在使用智能指针或容器(就像您应该使用的那样),那么可以。
        当智能指针超出范围时,内存被释放。

        std::auto_ptr<int> f()
        {
            int *p = new int;
            return p; // smart pointer credated here and returned.
                      // p should probably have been a smart pointer to start with
                      // But feeling lazy this morning.
        }
        
        std::vector<int> f1()
        {
            // If you want to allocate an array use a std::vector (or std::array from C++0x)
            return std::vector<int>(10);
        }
        
        int main()
        {
            std::auto_ptr<int> p = f();
            std::vector<int>   p1 = f1();
        
            //using p;
            return 0;  // p destroyed
        }
        

        即使不是,我能正常删除main中分配的[]内存吗?

        确保在不需要时立即正确释放所有内存是正常的。
        注意 delete []delete 是不同的,所以要小心使用它们。

        使用new 分配的内存必须使用delete 释放。
        使用new [] 分配的内存必须使用delete [] 释放。
        malloc/calloc/realloc 分配的内存必须用free 释放。

        问题似乎是关于确切数组大小的信息在那个时候丢失了。

        记住这些信息是运行时系统的问题。它是如何存储的,标准没有规定,但通常接近分配的对象。

        另外,在 malloc 和 free 的情况下答案会改变吗?

        在 C++ 中,您可能不应该使用 malloc/free。但它们可以使用。使用时要一起使用,保证不泄露内存。

        【讨论】:

          【解决方案4】:

          你被误导了——局部变量被清理了,但分配给局部指针的内存却没有。如果你不返回指针,你会立即发生内存泄漏。

          不必担心编译器如何跟踪分配了多少元素,这是 C++ 标准未解决的实现细节。只知道它有效。 (只要你使用 delete[] 符号,你就这样做了)

          【讨论】:

          • 真的是编译器还是操作系统在跟踪分配的字节数?
          • @Blagovest Buyukliev,编译器负责。如果它将责任委托给操作系统,那就是它的业务。
          【解决方案5】:

          当您使用new[] 时,编译器会添加额外的簿记信息,以便知道delete[] 有多少元素。 (以类似的方式,当您使用malloc 时,它知道free 有多少字节。一些编译器库提供扩展来找出该大小是多少。)

          【讨论】:

            【解决方案6】:

            我还没有听说过编译器会这样做,但是编译器肯定可以检测(在许多情况下)从函数分配的内存是否不再被指针引用,然后释放该内存。

            但是,在您的情况下,内存不会丢失,因为您保留了一个指向它的指针,该指针是函数的返回值。

            内存泄漏的一个非常常见的情况和此类功能的完美候选者将是以下代码:

            int *f()
            {
                int *p = new int[10];
                // do something that doesn't pass p to external
                // functions or assign p to global data
                return p;
            }
            
            int main()
            {
                while (1) {
                  f();
                }
                return 0;
            }
            

            如您所见,指向已分配内存的指针丢失了,编译器可以绝对确定地检测到。

            【讨论】:

            • 编译器肯定不可能确定这一点。如果指向分配块的指针被交给另一个翻译单元中的函数怎么办?该函数可以对指针做任何事情,包括制作static 副本。事实上,编译器甚至不需要知道内存来自newmalloc。这是为 C/C++ 编写垃圾收集器的方式非常困难。
            • 这里很容易作弊,比如 p1 = p; p = 空;返回 p1;或以其他方式返回指针。
            • @larsmans: 是的,但在大多数典型情况下,例如int *f() { int *p = new int[10]; return NULL; },编译器可以做到。
            • 编译器只有在它能够证明没有可能的对指针的有效引用时才能这样做。如果您已将指针传递给任何外部函数,这基本上是不可能的。并且使用过printf("%p", ptr); 完全排除了它。
            • @R..:通常是正确的,但是许多编译器被实现来理解标准库中函数的语义,因此类似printf 的函数不应该保持静态指向其参数的指针。我并不是说在所有情况下都可以检测到这一点,只是在某些情况下可以绝对确定地检测到。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-06-22
            • 2021-11-03
            • 2013-03-03
            • 1970-01-01
            • 2013-09-07
            • 2011-09-19
            • 1970-01-01
            相关资源
            最近更新 更多