【C语言】------ 动态内存分配


动态内存分配

什么是动态内存分配?

所谓的动态内存分配,就是在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需求即时分配


一、为什么使用动态内存分配呢?

示例:当我们在声明一个数组的时候,我们需要指定数组的长度,它所需要的内存编译时分配。但是在程序运行的时候我们才可能知道真实所需要的长度,因为它所需要的内存空间取决于输入的数据

char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

这种方法的优点是很简单,但是如果程序需要使用的元素数量超过了声明的长度,就无法处理这种情况。那怎么办呢?我们很容易就想到那就把数组声明的大一点,这确实是一个办法,然而这也会使得很大部分的内存空间浪费掉了,所以我们引出动态内存分配

我们先看看内存的分区
在这里插入图片描述

二、动态内存函数

1.malloc和free

C语言函数库提供了俩个函数,mallocfree,分别用来执行动态内存的分配和释放,当一个程序需要内存时,就会调用malloc函数,malloc从内存池()中提取一块内存,并向该程序返回一个指向这块内存的指针(此块内存此时并未初始化)。

void* malloc (size_t size);
void free(void* ptr);

1.它们的头文件在stdlib.h中声明,malloc的参数是需要分配的内存字节数,若开辟成功,则返回一个指向被开辟的内存块的起始位置的指针,注意malloc所分配的是一块连续的内存;如果内存池是空的或者它的可用内存无法满足你的请求的时候,malloc则返回一个NULL空指针,所以对每个malloc返回的指针都应该进行检查。
由于返回值的类型是void*,所以mall函数并不知道开辟空间的类型是整型、浮点型、结构或者是数组,具体使用的时候可以转换为其他任何类型的指针。
2.free函数专门用来做动态内存的释放和回收的:

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

我们看下面这个例子:

int main()
{
	
	//int arr[10] = { 0 };

	//动态内存开辟
	int* p = (int*)malloc(40);
	if (p == NULL)//判断p是否为空指针
	{
		printf("%s\n", strerror(errno));
		return 1;//异常返回
	}
	//使用内存
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
		printf("%d ", *(p + i));
	}

	//没有free
	//当程序退出的时候,系统自动回收内存空间
	free(p);
	p = NULL;
	return 0;//正常返回
}

判断p不为空指针是进行(使用代码)
在这里插入图片描述
将申请的空间内存释放回收:

free( p);
p = NULL;

在这里插入图片描述

2.calloc和realloc

(1)calloc函数也用于内存分配,它与malloc之间的主要区别在于calloc返回地址之前把申请的空间的每个字节初始化为全0。另外一个小的区别在于它们请求内存数量的方式不同。calloc的参数包括所需要元素的数量和每个元素的字节数,根据这些值计算出需要开辟的内存

void* calloc (size_t num, size_t size);

例如:

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//打印
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

我们看这段代码运行的结果:
在这里插入图片描述
我们什么也没有做,但是代码打印的值全被赋成了0,说明calloc函数在开辟好空间之后,把内容初始化成0,然后把起始地址返回来。
所以在calloc和malloc之间如何选择我们就有了一点的了解,如果你需要初始化则选择calloc。
(2)realloc函数用于修饰一个原先已经分配的内存块的大小,它可以使一块内存块扩大或者缩小:
realloc函数原型:

void* realloc (void* ptr, size_t size);

  • ptr是要调整的内存地址
  • size是调整后的新的大小

1.当用于扩大一个内存块时,这块内存原先的内容将依然保留,新增加的内存添加到原先内存块的后面,且新内存并没有进行初始化。

2.当缩小一块内存的时候,该内存块尾部的部分内存被拿掉,剩下的原先内容保留不变。
3. 当原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先内存的内容复制到新的内存块上。所以在使用realloc之后,不能再使用指向旧的内存的指针,而是该用realloc所返回的新指针。

在这里插入图片描述

举个例子:

int main()
{
	int* p = (int*)malloc(40);
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用  1 2 3 4 5 6 7 8 9 10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//扩容
	int* ptr = (int*)realloc(p, 80); //用ptr来接收
	if (ptr != NULL)
	{
		p = ptr;//p得到有效的地址(/新)
	}
	//使用
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放 
	free(p);
	p = NULL;

	return 0;

在这里插入图片描述

三、常见的动态内存错误

1.对NULL指针的解引用操作

不检查从malloc函数返回的指针是否为空

void test()
{
 int *p = (int *)malloc(20);
 *p = 20;//如果p的值是NULL,会出错
 free(p);
}

2.对动态内存开辟的越界访问

访问动态分配的内存之外的区域

void test()
{
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 		printf("%s\n", strerror(errno));
		return 1;
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//当i是10的时候越界访问
 }
 free(p);
 p=NULL:
 return 0;
}

3.向free传递一个非malloc函数返回的指针

对非动态内存开辟使用free释放

void test()
{
 int a = 10;
 int *p = &a;
 free(p);//ok?
}

4.动态开辟内存忘记释放造成内存泄漏

void TEST()
{
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
}
int main()
{
 TEST();
 return 0}

内存泄漏会增加程序的体积,可能导致程序或系统崩溃,所以

一定要正确的释放内存


总结

1.malloc和calloc函数用于动态分配一块内存,并返回一个指向该内存的指针,malloc的参数就是需要分配的内存的字节数,而calloc的参数时需要分配的元素个数和每个元素的长度;calloc函数在返回时将内存初始化为0,而malloc则不会进行初始化。

2.realloc函数可以调整动态内存分配的大小。以及出现的两种情况

3.如果请求内存开辟失败,malloc、calloc和realloc函数返回的是一个NULL指针。若一个指针不是从这些函数返回的,则它不能作为参数传递给free函数,同时,我们也不能只释放一块内存的一部分。

到这里,相信大家对动态内存分配有了一定的了解与掌握吧!我在这里是把我所学习到的和书上的知识内容做个总结与叙述,希望能有所帮助,谢谢。

  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FIRM'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值