如果使用malloc分配的内存被使用delete删除而不是free,会发生什么?

8
我遇到了一个无法解决的问题。
我的问题是,如果我使用malloc来分配内存,然后使用delete删除内存块呢? 通常的规则是:

如果我们使用malloc分配内存,则应使用free进行删除。

如果我们使用new分配内存,则应使用delete进行删除。

现在,为了检查如果我们反过来做会发生什么,我写了一段小代码。
#include<iostream>
#include<cstdio>
#include<cstdlib>

using namespace std;

class A
{
  int p=10;
public:
  int lol() {
    return p;
  }
};

int main()
{
  int *a = (int *)malloc(sizeof(int)*5);
  for(int i=0; i<4; i++) {
    a[i] = i;
    cout << a[i] << endl;
  }

  delete(a);
  // Works fine till here..

  A b;
  cout << b.lol() << endl;
  free(b); // Causes error.     

  return 0;
}

我遇到的错误是:

错误:不能将‘A’转换为类型为‘void*’的参数1,然后传递给‘void free(void*)’

我无法理解为什么会出现这种情况。请解释一下。

1
b的类型是A类。它不是指针。因此,您不能释放它。 - Barış Akkurt
2
首先,您实际上不使用 new 来分配 b - Some programmer dude
4
如果你更正代码,尝试在使用new分配的内存上调用free()(反之亦然),将会导致未定义行为,并且任何事情都可能发生。 - Some programmer dude
1
delete()/free()需要一个指针作为参数。 - πάντα ῥεῖ
1
因为这些是专门为C++开发的工具,更好地适应了它的习惯用法和模式。例如,它们会自动调用构造函数和析构函数。 - Alexander Shukaev
显示剩余5条评论
8个回答

24
当您调用delete删除指针时,编译器会自动为您调用类的dtor,但free不会。(同样,new将调用类的ctormalloc则不会。)
在您的示例中,char数组显然没有dtor,因此delete什么也不做,只返回内存。这就是为什么它可以的原因。(反之亦然,new一个char数组然后free它。)
但仍然可能会有问题:如果newmalloc从不同的堆管理器借用内存怎么办?
想象一下,你向A借钱,稍后又向B归还。即使你没关系,你考虑过A的感受吗?
顺便说一下,您应该使用delete [] pArray;来释放使用new[]分配的数组。

6
免费函数需要指向由malloc分配的内存的指针。编译器只是告诉你它没有传递一个指针,这是它可以轻易证明的事实。我猜你想用new创建一个对象,然后调用该对象地址上的free。但你没有调用new。
当你改正这个问题时,你可能会发现free成功了,但你不应该对这样的成功读取任何重要信息。你知道规则,它们在你的文档中清楚地说明了。做你正在尝试的事情是不正确的。这种行为是未定义的。这样的实验有很少的收获。你不会学到C内存管理函数为什么不能与C++内存管理函数配对的原因。
你的实验问题在于其没有效力。你只能展示一个特定情况的结果。但这些函数是设计成可以全面工作的。你如何测试每一个可能的情况?
永远不要使用试错法进行编程。你必须始终理解你所做的背后的规则和原则。

这是其中一个原因。我在这里的动机是想知道为什么malloc和delete不能互换使用? - Krishnachandra Sharma
1
你的程序不会带领你获得任何启示。它们不能互换,因为规范是这样规定的。 - David Heffernan

5

你的b变量不是一个指针,而是一个在栈上分配的A。如果你想尝试这个实验,那么你需要这样说:

A* b = new A;
free(b);

我非常赞成尝试新事物,但是我不确定你在这里想要达到什么目的。

在C++中,operator new不仅会分配内存,还会运行构造函数。同样地,delete会为您运行析构函数(考虑多态类),并释放返回的内存。另一方面,malloc()free()只是C库例程,从程序员的角度来看,它们不做任何多余的工作,只请求操作系统分配一个内存块,然后将其返回。

现在,对于一个简单的POD类,具有平凡的构造函数和析构函数,您可能可以混用C和C++内存分配,因为没有C++特定的事情需要完成。但即使您这样做了,这证明了什么呢?它是未定义的行为,并且可能在另一个编译器上无法工作。明天它也可能无法工作。

如果您使用任何C++特性——继承、虚函数等等,它肯定不会工作,因为newdelete必须完成比分配内存更多的工作。

所以,请遵守规则! :-)


4
您没有使用new创建,因此您没有测试所需的假设。(另外,b的名称不是指针。) 无论如何,只有在它们的实现可以互换时,malloc/newdelete/free才能互换使用。在简单情况和较简单的平台上,这更可能发生,但长期或习惯性地这样做是冒险的。例如,对于具有非平凡析构函数的类应用于数组形式的new,需要实现(编译器、运行时等)记住数组的大小,以便delete可以为每个数组对象调用析构函数。几乎没有其他地方可以放置这样的信息,除了动态分配的块本身,在new返回的指针引用的内存位置之前。将该指针传递给free将要求内存分配器确定分配块的实际起始位置,这可能是可以或不可以做到的。更一般地说,newmalloc可能会引用完全不同的内存分配器。编译器、运行时库和操作系统共同为您的程序提供内存。由于切换或升级这些组件,细节可能会发生变化。

2

文档中得知:

void free (void* ptr); 

ptr::

指向之前使用malloc、calloc或realloc分配的内存块的指针。

而您传递的不是指针,因此它会报错。

 A *b = new A();
 cout << b->lol() << endl;
 free(b); // Will Not cause error

1
不,并非一个经验法则是你不应该混合使用new/free或malloc/delete。
当然,可能可以使用free释放你用new获得的内存分配而不会崩溃,但这高度依赖于实现和系统。
忽略绕过析构函数或编写将在某些系统上破坏内存的代码的事实,更基本、更基础的C++考虑只是这样:
malloc/free是全局系统函数,而new和delete是运算符
struct X {
    void* operator new(size_t);
    void operator delete(void* object);
};

当然,你可以从亚马逊借一本书并将其归还到你当地的图书馆:在深夜去那里,把书放在他们的前门口。
这将导致未定义的行为,包括你被捕 - 我说的是在此处混合使用new/free :)
换句话说: malloc 和 free 处理原始内存分配,而 new 和 delete 处理对象,这是非常不同的事情。
void* p = new std::vector<int>(100);
void* q = malloc(sizeof(*q));

1
已经给出了很多好的理由,我会再添加一个原因:在C++中可以重载new运算符。这允许用户指定自己的内存管理模型(如果他们愿意的话)。虽然这很不常见,但它的存在只是malloc和new、delete和free不能互换使用的另一个原因。这是@Patz所描述的原因。

看起来@kfsone比我先完成了。 - Thayne

0

free()接受一个指针。你正在将一个对象(而不是指针)传递给free。这就是为什么会出现错误。如果你进行以下更改,你的代码将正常工作。

A *a = new A;
free(a);

尽管上述代码运行良好,但会导致内存泄漏,因为free()不调用析构函数,而后者负责释放构造函数创建的资源。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接