C进阶—动态内存管理

目录

一、动态内存管理的原因

二、堆区分配内存

三、动态空间操作(如何申请空间、如何释放)

(二)void *malloc(size_t size)

(二)void free(void *start)

(三)void *calloc(size_t num,size_t size )

(四)void*realloc(void *memblock ,size_t size)

四、常见的动态内存错误

五、动态内存管理注意事项

六、柔性数组

(一)柔性数组的定义

(二)柔性数组的特点

(三)柔性数组的使用

(四)柔性数组的优势


一、动态内存管理的原因

1.变量名称是一种外在体现,是给程序员看的,程序内部指令都是通过地址进行操作的

2.每个函数在调用的时候都会分配一块栈空间,作为函数的局部存储,函数内定义的局部变量,使用空间都是在函数栈空间中分配的。

一个 函数内其实如果定义局部变量并不是无限制定义的,如果定义一个超大变量,超过了函数空间大小就会报错。

在一个函数中定义一个变量的注意事项:

局部变量:栈空间上内存分

1.不能过大,会超出函数栈空间

2.变量出了作用域空间就会被释放

3.当我们有一些需求:保存一组数据,但是也不知道数据有多少个,因此只能按照上限个数提前定义好数组,但是会造成空间浪费,很多实际情况用不了。基于以上的局部变量的一些限制,提出堆上内存分配。

二、堆区分配内存

                                                                                                    0xFFFFFFFF

栈    分配地址从高往低,先定义的变量地址高。空间大小不固定,程序编译阶         段无法确定。如函数递归,栈空间不断分配。

                            

堆     分配地址从低往高,先定义的变量地址低。空间大小不固定,程序编译             阶段无法确定。如动态空间申请。
数据段    所占用空间大小在编译完毕后是固定的
代码段    所占用空间大小在编译完毕后是固定的
                                                                                                    0x00000000

在堆区分配内存——动态内存分配——分内存有个特点:

1.手动申请,手动释放(出了申请位置的作用域也不会释放)

2.需要多少申请多少(按需分配)

3.空间在堆上分配,不占用栈空间(因此不怕栈溢出)

三、动态空间操作(如何申请空间、如何释放)

(二)void *malloc(size_t size)

功能:申请size字节大小的空间

返回值:成功则返回申请到的空间的首地址;失败则返回NULL

注意:在动态空间申请的时候一定要进行返回值的判断,因为空间申请有可能失败

(二)void free(void *start)

功能:释放动态申请的空间参数,参数start是动态申请空间时返回的首地址

注意:1.不要对局部变量进行free;2.空间释放只能从动态申请的首地址释放,不能是从中途释

动态地址分配,申请空间是一整块连续空间,因此可以当做数组来用

(三)void *calloc(size_t num,size_t size )

功能:为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

返回值:成功则返回申请到的空间的首地址;失败则返回NULL

(四)void*realloc(void *memblock ,size_t size)

功能:重新为原来的空间扩容。原先使用的空间必须有足够的空闲空间,否则就申请新的空间,将数据拷贝过去,返回新地址。

memblock:原先老的空间首地址;          size:要扩容到的大小;

注意:成功则会释放原先的空间,返回新的空间首地址(在原地址扩容成功则不会释放),失败返回NULL。realloc如果传入的源空间地址为空则相当于malloc。

扩容的两种情况:

情况1:原有空间之后有足够大的空间

要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

情况2:原有空间之后没有足够大的空间

原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来
使用。这样函数返回的是一个新的内存地址。

四、常见的动态内存错误

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

2.对动态开辟空间的越界访问

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

4.使用free释放一块动态开辟内存的一部分

5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存泄漏)

ps:

野指针:指向了一块不能正常访问的空间

如果申请了一块内存,将地址赋值给了指针变量p,然后对p进行free,这时候只是把指针p指向的空间释放了并没有修改p变量的指向。意味着这首p指针就是一个野指针。

五、动态内存管理注意事项

malloc和free一定成对出现。

1.不要对局部变量的地址进行释放

2.动态申请的空间不要从中释放

3.释放了指针变量指向的空间后,指针变量会变为野指针,要注意指向,不要继续访问原员空间,

4.动态申请的空间不要重复释放

5.扩容成功后,有可能原空间会被释放掉,因此一定要保存新的地址

6.申请与释放一定要成对出现

六、柔性数组

(一)柔性数组的定义

简单理解不是一个数组,是和结构体搭配使用的一种数据结构。

定义:在一个结构体中。定义一个没有大小的数组,数组的空间通过动态内存申请。

例:

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;

(二)柔性数组的特点

1.结构中的柔性数组成员前面必须至少一个其他成员,柔性数组要放在结构体的最后边。
2.sizeof 返回的这种结构大小不包括柔性数组的内存。
3.包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

(三)柔性数组的使用

int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);

(四)柔性数组的优势

1.方便内存释放。

2.这样有利于访问速度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小熊爱吃软糖吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值