C语言中可以使用malloc、calloc、realloc来开辟堆区内存,使用free来释放堆区内存
C++中用new来开辟堆区内存,在堆区开辟的数据需要程序员手动释放,用delete来释放
C语言中的内存操作关键字
malloc
void* malloc (size_t size);
这个函数可以用来向内存申请一块连续可用的空间,并返回指向这块空间的指针
- 如果开辟成功,则返回一个指向开辟好空间的指针
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
- 返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定
- 如果参数size 为0,malloc的行为标准是未定义的,取决于编译器
使用示例:
// malloc了一块大小为一个整型的空间,将其void*的返回值强转为int*,赋给int*类型的变量p1接收
int* p1 = (int*)malloc(sizeof(int));
// malloc了一块大小为8个整型的空间,将其void*的返回值强转为char*,赋给char*类型的变量p2接收
char* p2 = (char*)malloc(sizeof(int)*8);
calloc
void* calloc (size_t num, size_t size);
- 函数的功能是为num 个大小为size 的元素开辟一块空间,并且把空间的每个字节初始化为0
- 与函数malloc 的区别只在于calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
如果对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务
使用示例:
// calloc了6块大小为一个整型的空间,共6个字节,将其全部初始化为0,并将其void*的返回值强转为int*,赋给int*类型的变量p3
int* p3 = (int*)calloc(6, sizeof(int));
// calloc了8块大小为5个整型的空间,共40个字节,将其全部初始化为0,并将其void*的返回值强转为char*,赋给char*类型的变量p4
char* p4 = (char*)calloc(5, sizeof(int) * 8);
realloc
realloc函数的出现让动态内存管理更加灵活
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了。为了合理的使用内存,我们一定会对内存的大小做灵活的调整。realloc 函数就可以做到对动态开辟内存大小的调整
void* realloc (void* ptr, size_t size);
- ptr 是要调整的内存地址
- size 调整之后新大小
- 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
realloc在调整内存空间时存在4种情况:
- 调整之后的内存 < 原空间大小:原空间尾部的内存被拿掉,只剩余调整之后的大小,内容只保留调整之后的部分
- 调整之后的内存 = 原空间大小:原空间大小和其中的内容不发生改变
- 调整之后的内存 > 原空间大小,且原空间后面有足够的连续空间:在原空间后面开辟新增的空间大小
- 调整之后的内存 > 原空间大小,且原空间后面没有足够的连续空间:重新开辟一块调整之后大小的连续空间,把原空间的数据拷进去
因为realloc在申请对空间对原空间进行扩容时,可能存在后面没有足够连续空间的情况,因此使用方法和前面几个有所区别:
// malloc了一块大小为20字节的空间,将其void*的返回值强转为int*,赋给int*类型的变量p5
int* p5 = (int*)malloc(20);
// 将p5的内存扩大到1000字节
p5 = (int*)realloc(p5, 1000);
// 上面这段代码看似没有什么问题,其实不然;若原来的p5空间后面没有连续的980字节供realloc扩容
// 则realloc会在内存中重新寻找一块1000字节的连续空间,把原空间的数据拷进去,再把新空间的名字改为p5
// 此时原空间的数据没有释放掉,若不小心对这段空间进行访问,就会发生意想不到的情况
// 正确的用法是这样的:
// malloc了一块大小为20字节的空间,将其void*的返回值强转为int*,赋给int*类型的变量p6
int* p6 = (int*)malloc(20);
int* p7 = NULL;
// 将p6的内存扩大到1000字节,并赋给p7
p7 = (int*)realloc(p6, 1000);
if (p7 != NULL)
{
p6 = p7; // 再把p7给p6
}
free
void free (void* ptr);
free函数用来释放动态开辟的内存,动态开辟的空间一定要释放,并且正确释放。
- 如果参数ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的
- 如果参数ptr 是NULL指针,则函数什么事都不做
double* p = (double*)malloc(sizeof(int) * 40);
free(p);
C++中的内存操作关键字
申请和释放单个元素的空间,使用new和delete操作符;申请和释放连续的空间,使用new[ ]和delete[ ]
new
语法:new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete
delete的使用方法:
int* ptr4 = new int;
int* ptr5 = new int(10);
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
其实,C++中的new和delete关键字在底层仍是由malloc和free通过operator重载实现的
总结
malloc/free | new/delete |
---|---|
malloc/free是函数 | new/delete是运算符 |
malloc可以按字节申请,得到的指针无类型,使用时一般需要强转 | new在申请时就写明了类型,得到的指针有类型,不需要强转 |
malloc申请失败时,返回NULL,因此需要判空 | new申请空间失败时,抛异常 |
申请自定义类型空间时,malloc/free不会调用构造/析构函数 | 申请自定义类型空间时,new/delete会调用构造/析构函数进行初始化和清理工作 |
使用malloc申请的内存,理论上可用delete释放,但可读性很差,因此需要配对使用 | 使用new申请的内存,若使用free释放可能会因无法执行析构函数而出错 |