【C/C++】动态内存管理详解

本文详细介绍了C语言中的malloc、calloc、realloc和free函数,以及C++中的new和delete操作符,对比了它们在内存分配和释放上的差异。重点讲解了如何在C语言中动态分配和调整内存大小,以及C++中自定义类型空间的初始化和清理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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种情况:

  1. 调整之后的内存 < 原空间大小:原空间尾部的内存被拿掉,只剩余调整之后的大小,内容只保留调整之后的部分
  2. 调整之后的内存 = 原空间大小:原空间大小和其中的内容不发生改变
  3. 调整之后的内存 > 原空间大小,且原空间后面有足够的连续空间:在原空间后面开辟新增的空间大小
  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/freenew/delete
malloc/free是函数new/delete是运算符
malloc可以按字节申请,得到的指针无类型,使用时一般需要强转new在申请时就写明了类型,得到的指针有类型,不需要强转
malloc申请失败时,返回NULL,因此需要判空new申请空间失败时,抛异常
申请自定义类型空间时,malloc/free不会调用构造/析构函数申请自定义类型空间时,new/delete会调用构造/析构函数进行初始化和清理工作
使用malloc申请的内存,理论上可用delete释放,但可读性很差,因此需要配对使用使用new申请的内存,若使用free释放可能会因无法执行析构函数而出错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值