【C语言】动态内存管理

本文探讨了C语言中动态内存管理的重要性,介绍了malloc、calloc、realloc和free四个关键函数的使用,阐述了动态内存分配的优缺点及内存分布。同时讲解了柔性数组的概念、特点及其在实际操作中的应用。
摘要由CSDN通过智能技术生成

文章前言:

  本文主要介绍C语言中的动态内存管理,主要是4个与之相关的函数。另外,我认为柔性数组也可以归于动态管理中,但其是与结构体密切相关的,建议可以先了解结构体再看柔性数组。

动态内存管理:

为什么会有动态内存管理?

在正式介绍动态内存管理之前,我们应该要先想想为什么需要它?
请看下列代码:

//通讯录
typedef struct person
{
	char name[20];
	char sex[5];
	int age;
	char num[15];
	char adrs[30];
}person;

int main()
{
    //创立通讯录
    person A[10] = {0};
}

联合实际上看有没有看出什么问题?
有,而且是很基础的问题——通讯录里面的可以添加的人物太少了!

但有人会说,那直接在代码里初始化一个比较大的数不就可以解决这个问题了么?
很好,但如果这么想的话,我一旦设为1000,甚至1000000,结果在某一段时间用的不过几十余人而已,那又会造成大量的内存浪费,这产生了一个新问题。

因此,我们的主角——动态内存管理 响亮登场!

动态内存分布:

在前言我已经提到动态内存管理是与四个函数有着密切联系,这四个函数的其中三个可以在堆中创建变量。我先给出“内存图”,看看动态内存管理函数在什么地方上大方异彩。

在这里插入图片描述
上图是C语言的大概内存分布图,可以看见,动态内存是在堆区上开辟的。
在继续往下介绍前,各位可以先简单想想:动态内存的开辟有什么弊端?

malloc():

函数声明:
void* malloc (size_t size);

可以看出:
1、malloc函数的参数只有一个,那就是size,这个表示要开辟内存有 size个字节大小。
2、malloc函数会返回一个void* 的指针,在使用特定的指针来接收前,必须先将malloc返回的结果强制类型转换

//针对上面的结构体
person*p = (person*)malloc(5 * sizeof(struct person));
//这里表示创建5个person类型大小的空间

好,这就已经创建完毕了!
让我们接着看下面的函数吧!

calloc():

函数声明:
void* calloc (size_t num, size_t size);

可以看出:
1、calloc函数有两个参数,第一个参数num表示创建多少次,第二个参数表示一次创建多少字节
2、calloc函数会返回一个void* 的指针,在使用特定的指针来接收前,必须先将malloc返回的结果强制类型转换。这一点与malloc一样。

person*p = (person*)malloc(5 , sizeof(struct person));

华生,发生什么盲点了吗?
与malloc的实现代码相比将呢?
没错,这两个表面上很像!
都是在堆上创建5个struct person大小的空间!
但是它们在具体上有什么区别吗?
有——malloc只是创建空间,而calloc不仅创建空间,还会将里面的数据初始化为0。具体使用什么函数,希望你可以根据情况而来,各有各用途。

realloc():

前面两个函数应该可以很好地创建函数了,那这个函数用来干嘛?
还是看上面代码:

person*p = (person*)malloc(5 * sizeof(struct person));

这时已经开辟好了空间,但我想改变空间大小且保留其中内容,怎么办?
再用上面两个函数就无法实现了吧,这时就需要realloc函数了。
在我的想法中,感觉realloc才是动态内存管理的核心,它是最能体现“变”的函数。

函数声明:
void* realloc (void* ptr, size_t size);

可以看出:
1、realloc函数有两个参数。
第一个参数是一个void指针,用来传递之前已经指向动态内存的指针;
第二个参数是一个整数,表示开辟多少字节大小的空间。
2、realloc函数会返回一个void
的指针,在使用特定的指针来接收前,必须先将realloc返回的结果强制类型转换。这一点与malloc和calloc一样。

person* p1 = (person*)realloc(p, 10 * sizeof(person));
//变大了,而且没改变原有的数据
函数大致实现逻辑:

realloc函数在执行时根据之后的空间大小改变后的大小有两种执行方案。
1、如果原有空间之后有足够大的空间
那么就直接添加就好。
2、原有空间之后没有足够大的空间
malloc会向其它充足空间申请开辟(一定是成功开辟),并会将原数据都复制到新空间去,最后会返回这个空间的起始地址并销毁原空间。

free():

前三个函数已经可以很好地开辟动态内存空间了,那这个函数是做甚???
这个函数可以说是上面三个函数的伴生兄弟!在使用上面的三个函数后,如果你不想用已经开辟好了的空间,请一定要销毁原空间,尤其是在函数中创建的空间,一定要用指针保存地址或者销毁。

话说得这么多,你应该知道这个函数就是用来释放空间的。

person*p2 = (person*)malloc(5 * sizeof(struct person));

//想要实现的具体代码
//
//
//

//不想要这块内存了
//释放吧
free(p2);
//请一定要把原本指向动态内存的指针设为空指针,否则该指针就是野指针,不知道指向内存那一块
p2 = NULL;

上述代码是使用动态内存管理的大致步骤,当然可以有不同的写法,不过这种应该最简单且直接的了。

柔性数组:

什么是柔性数组?

结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

struct list
{
     int a;
     char b[];
     //char b[0]
     //这里的[0]和上面的[]一样,表示未知数组大小
     //因此b也就是我们所说的柔性数组
}

柔性数组的特点:

1、结构中的柔性数组成员前面必须至少一个其他成员。
2、sizeof 返回的这种结构大小不包括柔性数组的内存。
3、包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
第一条好懂,下面看第二、三条规则。

//第二条
typedef struct A
{
int i;
int a[0];//柔性数组成员
}A;
printf("%d\n", sizeof(A));
//输出的是4
//也就是说,sizeof操作符没有考虑柔性数组的大小,因为其根本未知,需要动态创建

//第三条
int i = 0;
A *p = (type_a*)malloc(sizeof(A)+100*sizeof(int));
//注意这里参数写的格式:结构体大小 + 开辟的柔性数组的大小

柔性函数的使用:

//承接上述代码
for(i=0; i<100; i++)
{
    p->a[i] = i;
}
free(p);

这样柔性数组不仅有了100个整形空间,还以递增的形式从0开始对空间进行赋值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值