C语言中有三个动态开辟空间的函数
1.malloc
void* malloc (size_t size);
开辟size个字节大小的内存,并返回所开辟空间的起始地址
2.calloc
void* calloc (size_t num, size_t size);
开辟num个大小为size的内存
并将里面的值都初始化为0,再返回所开辟空间的起始地址
3.recalloc
void* realloc (void* ptr, size_t size);
主要是用来扩容的
第一种情况:
当传过去的参数ptr为NULL时,相当于malloc
第二种情况:
在当前ptr所指的空间扩大到size个字节大小(前提ptr所指的位置后面有size个大小的字节可以用)
第三种情况:
另外找一块地方开辟size个字节大小,并且释放ptr所指的空间,,这个时候就要注意不能再使用ptr所指的空间了。(前提是ptr所指的位置后面空间小于size个字节,必须重新找一块连续的空间)
注意:使用realloc时一定要接收返回值。
以上的3个函数都用free来释放开辟的空间。
C++中的动态内存分配
int *p1 = new int;//申请一个整形大小的空间
int *p2 = new int(9);//申请一个整型大小的空间,并且将里面的值初始化为9
int *p3 = new int[5];//申请5个整形大小的空间(申请多个空间时,不可以初始化)
cout << *p2;
delete p1;
delete p2;//释放所申请的一个空间
delete[] p3;//释放所申请的多个空间
二、建议malloc与 free搭配使用,new 与delete搭配使用
1.内置类型的申请和释放不会出现问题
2.自定义类型可能会出现问题
(1).构造函数中没有动态申请内存,析构函数中没有释放内存
下面看到malloc和free只负责申请内存和释放内存
new会调用类的构造函数,Delete会调用析构函数
当不匹配使用时,可能就会出现只调用了构造函数,没有调用析构函数,如下图。
当然这里也并没有什么问题。因为这里的构造函数和析构函数并没有做什么事情。
(2)若构造函数中有动态申请内存,析构函数中有释放堆上开辟的内存
a.当构造函数中有用到动态内存的申请,并且在析构函数中就进行了释放,当用free进行释放时,它只是将我们申请的变量自己释放了,并没有将在构造函数中申请的空间进行释放,这样就会造成内存泄露。
class Date
{
public:
Date(int year=1900, int month=0, int day=0)\
: _year(year)\
, _month(month)\
, _day(day)
{
_a =(int *) malloc(sizeof(int)* 10);
cout << "构造函数被调用\n";
}
Date(Date const & date)
{
_year = date._year;
_month = date._month;
_day = date._day;
_a = (int *)malloc(sizeof(int)* 10);
cout << "拷贝构造函数被调用\n";
}
~Date()
{
free(_a);
cout << "析构函数被调用\n";
}
b.当申请个数与释放个数不匹配(自定义类型)(释放掉位置不正确)
当用new[n]来申请内存时,我们用[]里面的n可以知道要申请多少个自定义类型,但是释放时用delete[],如何来知道要释放几个自定义类型。其实是有如下机制,观察下图
事实上我们只是想申请80个字节,但是实际上却多开了4个字节,这4个字节就是用来存放[]中的n,这样的话,delete[],会自动向前找4个字节,读取到 n 的值,并且将这4个字节一起释放,所以可能出现释放的位置不正确而导致程序崩溃。
(这里的operator new[],见下面的解释)
1.new了一个自定义类型,却用delete[]来释放(由于delete[]多向前找了4个字节)
2.new[],new了多个自定义类型,却用delete只释放了一个(应该向前走4个字节却没有)
free是一定不会向前找4个字节的
但是当把数据成员做简单修改时,这个时候Date类的大小为4个字节,用free释放时,将指针减1,其实就时向前走所值内容的大小,刚好是4个字节,这个时候释放位置就正确了,是不是程序不会崩掉了?(嗯。好吧,我也觉得没有意义,就是突然想到了,是不是真的只跟释放的位置相关),发现下面的结果依旧是崩溃了,free() 机制释放时并不会去读取前面四个字节中的 n 啊,它只是得到了正确的位置,如何知道该释放几个对象大小的空间呢?
以上这些程序出错的情况在没有自定义析构函数,或者在申请或释放内置类型时都不会有任何问题。因为他们的构造函数或者析构函数并没有做一些开辟内存或者释放内存的事情。
operator new(),operator delete()其实是C++标准库函数,并不是new,delete的操作符重载,因为new ,delete是不可以重载的。
new的实现机制其实是调用ooperator new()
operator new()其实是调用malloc()如下图
总结一下
malloc是C语言中的库函数,需要手动传开辟空间的大小,失败返回NULL,错误形式是返回错误码,
operator new()是C++中的库函数,不需要手动传所要开辟空间的大小,错误形式是抛异常,
new是C++中的操作符,会调用构造函数
free只是进行空间的释放
delete会调用析构函数
结束