如果使用malloc分配的内存是使用delete而不是free删除的

What if, memory allocated using malloc is deleted using delete rather than free

本文关键字:删除 free 内存 malloc 分配 如果 delete      更新时间:2023-10-16

我遇到了一个无法解决的问题。

我的问题是,如果我使用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;
}

我得到的错误是:

错误:无法将参数"1"的"A"转换为"void*"自由(无效*)

我不明白为什么会发生这种事。请解释。

当您将delete调用为指针时,编译器会自动为您调用该类的dtor,但free不会。(此外,new将调用类的ctormalloc不会。)

在您的示例中,char数组显然没有dtor,所以delete只返回内存。这就是为什么它是好的。(反之亦然,新建一个char数组,然后释放它。)

但仍然可能存在一个问题:如果newmalloc从不同的堆管理器借用内存怎么办?

想象一下,你从A那里借钱,然后再还给B。即使你对此没意见,你有没有考虑过A的感受?

顺便说一句,您应该使用delete [] pArray;来释放使用new[]分配的数组。

free函数需要一个指向malloc分配的内存的指针。编译器只是简单地告诉您没有向它传递指针,这是一个很容易证明的事实。我猜你是想用new创建一个对象,然后在该对象的地址上调用free。但你并没有叫new。

当你纠正这一点时,你很可能会发现免费的成功,但你不应该从任何这样的成功中解读任何重要的东西。你知道这些规则,它们在你的文档中有明确的说明。做你想做的事是不正确的。这种行为是不明确的。从这样的实验中几乎没有什么收获。您将不会了解为什么C内存管理功能不能与C++内存管理功能配对。

你的实验的问题是它没有电。您可以只显示一个特定场景的结果。但这些功能是为全面发挥作用而设计的。你将如何测试每一个可能的场景?

切勿使用试错法编程。您必须始终了解所做操作背后的规则和原理。

您的b变量不是指针,而是分配给A的堆栈。如果你想尝试这个实验,那么你会说

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

我完全赞成实验,但我不确定你在这里想要实现什么。

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

现在,对于一个具有琐碎构造函数和析构函数的简单POD类,可能能够混淆C和C++内存分配,因为没有任何特定于C++的操作。但即使你这样做了,它能证明什么?这是未定义的行为,可能无法在其他编译器上运行。明天可能不行。

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

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

您没有使用new创建b,因此您没有测试预期的假设。(此外,b的名称不是指针。)

无论如何,malloc/newdelete/free只能互换工作,只要它们的实现是可互换的。在简单的情况下,在更简单的平台上,这种可能性更大,但从长远来看,这是一种冒险的赌注,或者是一种习惯。

例如,当应用于具有非平凡析构函数的类时,new的数组形式要求实现(编译器、运行时等)记住数组的大小,以便delete可以为每个数组对象调用析构函数。除了动态分配的块本身,在new返回的指针所指的存储器位置之前,几乎没有地方可以放置这样的信息。将该指针传递给free将需要内存分配器确定分配块的实际开始,这可能是它能够做到的,也可能是它不能做到的

更一般地,newmalloc可以指代完全不同的存储器分配器。编译器、运行库和操作系统协同工作,为程序提供内存。切换或升级这些组件中的任何一个都可能导致细节发生变化。

从文档中,

void free (void* ptr); 

ptr

指向以前使用malloc、calloc或realloc。

你传递的不是指针,所以它会抱怨。

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

不,不应该混合new/free或malloc/delete,这不是经验法则。

当然,使用free可以在不崩溃的情况下放弃使用new获得的分配,但这是高度特定于实现/系统的。

忽略了绕过析构函数或编写会破坏某些系统内存的代码这一事实,更基本、更基本的C++考虑因素实际上很简单:

malloc/free是全局系统函数,而new和delete是运算符

struct X {
    void* operator new(size_t);
    void operator delete(void* object);
};

当然,你可以从亚马逊借一本书,然后把它还给你当地的图书馆:一天深夜去那里,把它放在他们前门的台阶上。

它将有未定义的行为,直到并包括你被逮捕——我在这里谈论的是再次混合新的/免费的:)

换句话说:mallocfree处理原始内存分配,而newdelete处理对象,这是完全不同的。

void* p = new std::vector<int>(100);
void* q = malloc(sizeof(*q));

已经给出了许多好的理由。我还要补充一个原因:new运算符可以在c++中被覆盖。这允许用户指定他们自己的内存管理模型,如果他们愿意的话。尽管这很少见,但它的存在只是malloc和new、delete和free不能互换使用的又一个原因。这是因为@Patz描述的原因。

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

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

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