动态内存管理

本文介绍了动态内存管理的必要性,特别是针对数组大小未知的问题,以及C语言中malloc、calloc、realloc和free等内存管理函数的使用、注意事项和应用场景。通过实例演示了如何避免内存泄漏并确保内存的有效管理。
摘要由CSDN通过智能技术生成

为什么存在动态内存管理?

int a[20];
char a = 'foreb';

这是我们已经熟练掌握的内存开辟方式,但这种方式开辟的空间有以下特点:

1.空间的大小是固定的
2.申请数组时必须指定其大小

但往往我们会遇见这样的问题:所需要空间的大小只有在程序运行时才会知道。那刚才那种方式肯定行不通了,因为那方式不支持数组元素数目是变量,只能是常量。数组元素数目为常量会导致两个问题:

1.申请过多,浪费资源
2.申请过少,不够使用

那么此时为了解决这个问题,于是就有了动态内存管理

动态内存函数介绍

malloc

C语言提供的动态内存开辟函数:

void* malloc(size_t size)
//该函数向内存申请一块 连续可用  的空间,返回指向该空间的指针

a. 如果开辟成功,malloc返回一个指向某块空间的指针
b. 如果开辟失败,malloc返回NULL指针
c. 因为返回值类型为void*,所以malloc并不知道自己开辟空间的类型,怎么使用取决于使用者
d. 当输入参数size为0时,malloc的行为在标准中未定义,取决于编译器
e. malloc申请的空间是在堆区申请的。因为在内存地址空间栈区和堆区相向而生,所以malloc申请的空间大小也是有上界的。
f. 程序员用malloc申请的空间,要由程序员释放,否则会发生内存泄漏(内存泄漏问题只会在引起内存泄漏问题的程序运行期间存在,一旦该程序结束运行,内存泄漏问题会消失【操作系统强制回收】)
g. malloc是个函数,所以其申请空间的行为只会在程序运行期间发生,换句话说,堆空间的大小只能在程序运行期间得到确定
h. malloc实际申请的空间会比输入size多一些,多出来的部分用来记录【这个空间有多大,起始地址在哪,啥时申请的】一些这样的信息,可以认为是被申请空间的属性,就像买零食,零食的外包装上的【配料表,能量表,生产日期】这类一样。

介绍完动态开辟,f中提到的释放也该上场了:free

free

void* free(void* ptr)
//free用来释放动态开辟的内存

a. 若ptr指向的空间并不是动态开辟的,则free行为是未定义的
b. 若ptr是NULL指针,则free什么也不做
c. 可以注意到,free输入参数只有一个ptr,仅仅是一个指向申请空间的指针,那free怎么知道这块空间多大?不用担心,这些信息,由malloc实际申请多出来的那部分空间提供。
d. mallocfree都声明在stdlib.h头文件中。

举个栗子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int num = 0;
	int* ptr = NULL; //NULL赋初值【避免ptr成为野指针】
	printf("请输入想要申请的空间大小(以int为例)# ");
	scanf("%d",&num); //num在程序运行期间由用户输入,而不是提前被定下大小,动态的一种体现
	ptr = (int*)malloc(num * sizeof(int));//malloc申请4*num字节的空间
	if (NULL != ptr) {     //【必须检查是否申请成功】
		int i = 0;
		for (i = 0; i < num; i++) {
			*(ptr + i) = 0;		//申请的空间存放num个0(int)
		}
	}
	free(ptr);    //释放动态申请的空间  【必须有释放步骤,否则有内存泄漏风险】
	ptr = NULL;   //重新使ptr指向NULL,否则ptr会成为野指针 【必须要有,野指针危害不小】
	return 0;
}

calloc

calloc也是C语言提供的内存管理函数

void* calloc(size_t num,size_t size)
//calloc的作用是开辟num个大小为size的空间,并将所申请的num*size字节的空间初始化为0

calloc没什么说的,就是malloc的基础加上初始化步骤。存在即合理,虽然calloc就比malloc多一个初始化,但也有其适合的使用场景,比如你要申请一块初始内容已知的空间,就可以用calloc

realloc

void* realloc(void* ptr,size_t size)
//realloc使内存管理更加灵活

为什么会更加灵活?试想,malloc和calloc虽然实现了动态内存申请,但我们有没有想到,在这两个函数申请仍会发生申请过多/过少的情况?它们并没有解决这两个问题,只是解决了我们不必在编程时就确定所要申请空间大小而已。

realloc就是来解决这两个问题的!
先介绍realloc的一些情况:
a. ptr是要调整的内存地址
b. size为调整之后申请空间的大小
c. 返回值是调整大小之后空间的内存起始地址
d. 该函数调整原内存空间大小之后,还会将原来内存的数据移动到新空间
e. realloc在调整内存空间时存在两种情况:
e1. 原有空间之后有足够大的空间(扩容)
e2. 原有空间之后没有足够大的空间(扩容)
e3. 减容没什么好说的,直接调用就行了

?? 为什么d中会出现数据转移的情况,原因在于e

e1. 有足够大空间意味着要扩充的空间(其大小为输入大小-原空间大小)只需续在原空间后面就行了

e2. 为什么会发生原有空间之后空间不够大呢?原因在于不只你一个程序在运行啊,你申请了空间,那么就有可能其他程序在你后面也申请了空间,导致你后面空间不够大了,这时你该怎么办?你只能在别的程序后面重新申请一块本次输入大小的空间了。为什么?因为这些函数只有一个返回值:一个指针!一个指针不能把两个不连续的地址让你串起来用。所以只能重新申请一块本次输入大小的空间。好了,申请好了,完事了?原空间可是有数据的!你要是直接返回新空间地址,就会丢失数据,所以会发生数据转移。数据转移完成后,才能将新空间地址返回。

【warning】:reallocmalloccalloc用完后都要被free的,都要被检查是否申请成功的,否则各种错误让你烦不胜烦,尤其realloc返回值不要先赋给原空间的指针!,先检查是否成功!

栗子!

int main()
{
	int num = 0;
	int* ptr = NULL; //NULL赋初值【避免ptr成为野指针】
	printf("请输入想要申请的空间大小(以int为例)# ");
	scanf("%d",&num); //num在程序运行期间由用户输入,而不是提前被定下大小,动态的一种体现
	ptr = (int*)malloc(num * sizeof(int));//malloc申请4*num字节的空间
	if (NULL != ptr) {     //【必须检查是否申请成功】
		//业务处理
	}
	ptr = realloc(ptr, 1000);//将申请空间扩充到1000个字节
	//该行为严重要摒弃!申请失败就凉凉了,【空间没了!数据没了!】
	return 0;
}

正确行为:

int main()
{
	int num = 0;
	int* ptr = NULL; //NULL赋初值【避免ptr成为野指针】
	printf("请输入想要申请的空间大小(以int为例)# ");
	scanf("%d",&num); //num在程序运行期间由用户输入,而不是提前被定下大小,动态的一种体现
	ptr = (int*)malloc(num * sizeof(int));//malloc申请4*num字节的空间
	if (NULL != ptr) {     //【必须检查是否申请成功】
		//业务处理
	}
	//空间不够了,扩容
	int* p = NULL;
	p = realloc(ptr, 1000);//将申请空间扩充到1000个字节
	if (p != NULL) {//申请成功再把ptr指向新空间!
		ptr = p;
	}
	//继续业务处理
	free(ptr);//使用结束,释放空间
	return 0;
}

_alloc

void * _alloca(size_t size);

_alloca()栈上开辟空间不允许程序员手动free()释放,函数运行完系统自动回收

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值