在C ++中摆脱非动态内存

时间:2014-04-12 15:11:15

标签: c++ dynamic heap-memory

C / C ++中是否有一个函数可以摆脱非动态数据,类似于动态分配内存的函数free();

我尝试使用函数free(i);,但编译器报告错误:

invalid conversion from 'int' to 'void*' [-fpermissive]

在下面的代码中,我使用了free(&i);,编译器没有报告错误,但它也没有释放内存。

#include<iostream>
#include<stdlib.h>

int main()
{
    int i, n;
    cin >> n;
    for(i = 0; i < n; i++);
    cout << i << endl;
    free(&i);
    cout << i << endl;
    return 0;
}

输入15输出为:

15
15

Process returned 0 (0x0)   execution time : 11.068 s
Press any key to continue.

我收到了编译器的警告:

warning: attempt to free a non-heap object 'i' [-Wfree-nonheap-object]

4 个答案:

答案 0 :(得分:3)

始终使用补充分配方法的解除分配方法 对于堆栈变量,这意味着退出块,甚至可能是函数 否则你会得到未定义的行为,所以一切都会发生。

在您的示例中,free()似乎

  • 接受堆栈指针并破坏堆栈和堆管理结构,可能导致进一步的损坏,
  • 或者它检查传递的指针是否可以由相应的分配器返回,然后静默地执行任何操作。

对于调试,嘈杂的崩溃或至少明显的不当行为会更好,但无法保证。

答案 1 :(得分:3)

我会在这里添加一套初级答案,以防万一实际的新手偶然发现问题:

您不需要在堆栈中释放数据结构 - 正如Deduplicator所指出的那样 - 您也不允许这样做。除了一些特殊的在所有情况下,编译器将所有未动态分配的数据(例如,通过new)放在堆栈中。堆栈是运行时程序的内存部分,随着每个函数调用而增长,并随着每个函数退出而缩小。它分为所谓的堆栈帧,提供函数的本地内存。在编译时,编译器会找出函数需要多少内存 - 在您的示例中,对于两个4字节整数(假设您正在编译为32位目标)将为8个字节 - 并生成为所有人创建足够大的堆栈帧的指令调用函数时驻留的局部变量。但是,有一种方法可以告诉编译器在函数内部只需要有限时间的变量:由大括号创建的范围 - 正如niklasfi指出的那样。一个例子:

int foo() {
    int outerScopeVariable = 5;
    {
        int innerScopeVariableA = 8;
    }
    {
        int innerScopeVariableB = 20;
    }
}

变量innerScopeVariableA只会&#34;生活&#34;在它周围的花括号中。在高级别上,这意味着您无法在已声明的{}块之外的范围中引用它,并且对于块末尾的类,将调用该对象的析构函数。在较低级别上,编译器知道在块结束时不再需要为innerScopeVariableA保留的内存。因为它是堆栈内存,但它不能释放动态内存可以释放的方式。请记住,堆栈内存仅在函数末尾被丢弃。但它可以做的是为innerScopeVariableA重用innerScopeVariableB的内存。因此,对于foo,优化编译器实际上只需要8个字节的堆栈内存。

有关堆栈和堆的更多信息(这是分配动态内存的地方),您可以查看以下问题:What and where are the stack and heap?。可以在此处找到对堆栈的深入讨论:http://www.altdevblogaday.com/2011/12/14/c-c-low-level-curriculum-part-3-the-stack/


编辑:如果将大数据结构放在堆栈上,您实际上可以观察到此堆栈内存重用。运行使用 g ++ 4.8.1 编译的以下代码,您的输出为123987

#include <iostream>
using namespace std;

void foo() {
    {
        int a[1024];
        a[0] = 123987;
    }
    {
        int b[1024];
        cout << b[0] << endl;
    }
}

int main() {
    foo();
    return 0;
}

查看二进制g ++为堆栈上的foo保留4136个字节,其中4096个是一个包含1024个元素的整数数组所必需的(我已经为32位目标编译了)。额外的一点空间可能与内存对齐有关。您还可以通过打印内存地址来观察此效果,以下是使用在线编译器的示例:http://codepad.org/r5S1hvtV

答案 2 :(得分:1)

您只需将代码括在方括号{}中即可。任何对象都存在于堆栈中,直到周围的括号关闭。 E.g。

int b;
{
 int a;
} // a gets destroyed, b is still alive

正如Deduplicator所说,通常,你不需要处理变量的破坏,因为你的编译器会为你做这些。

答案 3 :(得分:1)

我不会手动调用析构函数,因为这是不好的做法,除非使用重载形式的运算符new()构造对象,除非使用std::nothrow重载。

摆脱堆栈记忆的常用习惯用法(并且技术上有资格作为你的问题的答案)是用默认构造的临时交换

MyType t1; 
std::swap(t1, MyType());

第二行用临时交换您的实例,因此原始实例在该行被破坏。

现在,您仍然在堆栈中留下了一个实例,因此有2个案例具有含义

  • 您希望在该行调用t1的析构函数(就像删除堆栈对象一样)
  • 默认构造类型要小得多,因此您需要clear and minimize