#include
using namespace std;
int main()
{
int* p1 = (int*)malloc(sizeof(int));
free(p1);
int* p2 = (int*)calloc(4, sizeof(int));
int* p3 = (int*)realloc(p2, sizeof(int) * 10);
free(p3);
}
C语言中,存在三个用于动态分配内存的函数 :
void *malloc(size_t size);
void *calloc(num,size_t size);
特点:num表示元素的数量,size表示每个元素的大小;分配的内存区域被初始化为0;分配失败返回NULL。
void *realloc(void *ptr, size_t new_size);
使用场景:
malloc
:当你需要分配一块内存,并且不需要初始化或者需要非零的初始值时。calloc
:当你需要分配一块内存,并且希望这块内存的内容是确定的(例如,初始化为 0)。realloc
:当你需要改变已经分配的内存块的大小,或者在程序运行时动态调整内存使用时。int* p1 = (int*)malloc(sizeof(int));
free(p1);
free函数用来释放通过malloc、calloc和realloc函数动态分配的内存区域;它只有一个参数——指向需要释放的内存的指针;没有任何返回值;当动态内存不再被需要时,应当使用free函数来释放这块内存避免内存泄漏。
C语言内存管理方式在C++里是可以继续使用的,但是C语言的动态内存分配函数在初始化方面有些无能为力。C语言的内存管理方式无法满足自定义类型的动态内存管理需要,类的初始化是需要调用构造函数,而C管理方式不能调用构造函数。
C++提出了新的内存管理方式:通过new和delete操作符进行动态内存管理。
void test()
{
//动态申请一个int类型的空间
int *ptr1 = new int;
//动态申请一个int类型的空间并出示化为10
int *ptr2 = mew(10);
//动态申请10个int类型的空间
int *ptr3 = mew int[10];
delete ptr1;
delete ptr2;
delete[] ptr3;
}
申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],一定要匹配起来使用。
#include
using namespace std;
class A
{
public:
A(int a = 10) :k(a)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int k;
};
int main()
{
A* ptr = new A[10];
delete[] ptr;
}
new/delete与malloc/free最大的区别就是,前者对于自定义类型除了开空间之外还会调用构造函数和析构函数。
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数。new操作符在底层调用operator new 全局函数来申请空间,对于自定义类型还会调用构造函数。
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
看不懂没关系,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间。
注意:这里的operator new不是重载的意思。
如果申请的是内置类型的空间,new和malloc,delete和free基本类似。不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]释放的是连续空间,而且new在申请空间失败时会抛出异常。
定位new表达式,是一种特殊的内存分配技术,它允许在已经分配的内存上构造对象。这种技术用于重用已经分配的内存,或者在特定的内存区域创建对象。
#include
using namespace std;
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
A* p1 = (A*)malloc(sizeof(A)*2);
// 注意:如果A类的构造函数有参数时,此处需要传参
new(p1)A;
//在内存的下一个位置构造对象。
new(p1 + 1)A;
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
//也可以显式调用
operator delete(p2);
return 0;
}
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的是:
内存泄漏是指因为疏忽或者错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段的控制,因而造成了内存的浪费。
内存泄漏会导致长期运行的程序响应越来越慢,最终卡死。
堆内存是指通过malloc/calloc/realloc/new等从堆中分配的一块内存,用完之后必须通过调用相应的free和delete释放。加入程序的设计错误导致没有被释放,那么这部分空间将无法再被使用,导致堆内存泄露。
指程序使用系统分配的资源,比如套接字、文件描述符、管道等没有使用相应的函数释放掉,导致系统资源的浪费,严重的可导致系统性能降低,系统执行不稳定。
delete[]是怎么知道要调用多少次delete函数的呢?
A* ptr = new A[10];
在这段代码中,实际上new[]不只申请了10个类类型的大小,它额外申请了4个字节用来存放开辟空间的数目,这四个字节在返回的地址之前。