动态内存管理知识点

动态内存管理

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

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

这是我们目前所掌握的开辟空间的方式

1、空间开辟的大小是固定的

2、数组在声明的时候,必须要指定数组的长度,它所需要的内存在编译阶段就分配

这种固定开辟空间的方式就有了局限性,不方便后期进行空间上的维护

动态内存函数

#include<stdlib.h> 

malloc

void* malloc (size_t size);

分配一个字节的内存块,返回指向该块开头的指针

1、如果开辟成功,返回一个指向开辟空间的指针

2、申请空间可能会申请失败,如果开辟失败,返回的是空指针,因此malloc的返回值一定要检查有效性

3、返回值的类型是void*类型,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者来决定

int arr[10]={0};
int *p = (int *)malloc(40);
if(p==NULL)
{
printf("%s\n",strerror(errno));
return 1;----历史习惯:return 0是正常返回,return 1就是异常返回
}
----当程序退出的时候,系统会自动回收内存空间,并不是说 内存空间就不回收了

free

void free (void* ptr);

free函数用来释放动态开辟的内存的

1、如果不是动态开辟的内存,是不能够用free来释放的

2、如果参数ptr是空指针,则函数什么事情都不会做

free是释放动态开辟的内存,然而p还记得free以前内存的地址,但此时如果再访问p指向的内存地址,会造成对非法空间访问的问题,也就演变成了野指针的问题,所以,对于动态开辟的内存释放以后,置为空指针是必须的。

calloc

void* calloc (size_t num, size_t size);
函数是开辟num个大小为size的空间
与malloc函数的区别就是calloc会在返回地址之前把申请到的空间的每个字节全部初始化为0
    也就是相当于calloc = malloc +memset

总结一句话,如果想要把申请到的内存空间初始化,就用calloc

如果不想初始化,就用malloc

realloc

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

1、ptr为要调整的内存地址

2、size为调整后的新大小

3、返回值为调整之后的内存地址

realloc函数调整内存空间的时候会有两种情况

1、原有空间后面没有足够大的空间

2、原有空间后面有足够大的空间1

int *ptr= malloc(20);
int *tmp =(int*) realloc(ptr,40);

1、没有足够空间,在堆空间其他位置找一个足够大小的空间,将原数据拷贝到这40个字节的空间中,并返回新开辟空间的起始地址

在这里插入图片描述

2、有足够空间,直接在后面追加20个字节的空间

在这里插入图片描述

不能直接用ptr来接收,如果要开辟的空间过大,堆上没办法开辟足够大的空间,realloc函数就要返回空指针,那样ptr就变成了空指针,以前的空间也找不到了

柔性数组

在C99标准中,结构中的最后一个元素允许是未知大小的数组

struct Stu
{
	int i;
	int arr[0];
}

在结构体成员中,最后一个成员是未知大小的数组成员,并且在柔型数组前面至少有一个元素

在计算大小的时候,只计算了柔性数组前面其他的成员大小

柔性数组的特点:

1、结构中的柔性数组前面至少有一个其他成员

2、sizeof返回的结构大小并不会包括柔性数组的内存

3、包含柔性数组成员的结构应该用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

🌰栗子:

struct Stu s;----不这样创建,这样的话只能存放4个字节,没有给柔性数组开辟空间

柔性数组的使用

int i=0;
struct Stu* p = (struct Stu*)malloc(sizeof(struct Stu)+10*sizeof(int));
if(p==NULL)
{
    return 1;
}
p->i=100;
for(i=0;i<100;i++)
{
    p->arr[i] = i;
}
free(p);

获得了100个整型元素的连续空间

如何柔性?可以通过realloc来维护空间

int i=0;
struct Stu* p = (struct Stu*)malloc(sizeof(struct Stu)+10*sizeof(int));
if(p==NULL)
{
    return 1;
}
p->i=100;
for(i=0;i<10;i++)
{
    p->arr[i] = i;
}
struct Stu* ptr = (struct Stu*)realloc(p,sizeof(struct Stu)+80);----扩容
    if(p!=NULL)
    {
        ps=ptr;
        ptr=NULL;
    }
free(ps);
ps=NULL;

那肯定会 有人问到了,为什么不能直接用int*类型的指针来维护一块动态内存空间呢?

struct S
{
	int n;
	int* arr;
};

int main()
{
	struct S*ps = (struct S*)malloc(sizeof(struct S));----因为动态开辟的内存是在堆区,所以我们把int类型的变量也创建在堆区
	if (ps == NULL)
	{
		return 1;
	}
	ps->n = 100;
	ps->arr = (int*)malloc(40);
	if (ps->arr == NULL)
	{
		//....
		return 1;
	}
	//使用
	int i = 0;
	
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	//扩容
	int*ptr = (int*)realloc(ps->arr, 80);
	if (ptr == NULL)
	{
		return 1;
	}
	else
	{  
		ps->arr = ptr;//补充:如果扩容成功,这里要讲ptr的值赋值给ps->arr,空间依然由ps->arr维护
	}
	//使用
	//释放
	free(ps->arr);
	free(ps);
	ps = NULL;
	return 0;
}

如果用int类型的指针开辟一块空间,第二种方法做了两次动态内存分配,在进行free释放结构体的时候,用户释放结构体,但不知道这个结构体中的成员也需要free,如果我们把结构体的内存以及成员需要的内存一次性分配好,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存释放掉。

并且,多次进行malloc还会造成内存碎片化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值