动态内存管理

一、为什么要动态内存分配

在c或c++中都会大量使用动态内存管理,同时使用C/C++实现数据结构时,也会使用动态内存管理

目前,申请内存的方式只有两种

1.1 在栈区申请内存

0b0cc57a931d496896bad4d7190742e2.png

 上面的内存申请方式,一旦申请好空间,大小无法调整

1.2 在堆区申请内存

为了引入第二种申请内存的方式,我们可以从一个问题看起“如何描述班级的学生的数学成绩”

首先我们可以想到使用数组来存储其成绩,int math[30]={0};当学生人数少于30时,空间够,但会造成浪费,当学生人数大于30时,申请的空间不够,无法达到使用需求。为了实现这种需求,引入了动态内存开辟,让程序员可以自己申请和释放空间。

二、malloc和free

2.1 malloc

函数原型如图

d4dcb3dc5cac4fb2a42a5ce0e99703bc.png

 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针(起始地址)。

  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查
  • 返回值类型为void*,函数并不知道开辟的空间所存储的数据类型,在使用时由使用者决定
  • 如果参数size为0,malloc的行为是标准是未定义,取决于编译器

15a6e08923e0447abaa521b70d9c1998.png

 如图申请了20个字节,来存储5个整数,在使用malloc前,需要包含头文件<stdlib.h>,在使用空间时,类似于数组。

27317df40f0f44eb838255487fd8168d.png

 05e64c110a5a4d99b17dd8d33182dd4d.png

通过查看内存,我们可以发现成功申请了20个字节,来存储1-5这五个整数 ,通过malloc等函数开辟的空间,均存储在堆区

42abb1f5efad44e6bbaaf379b809e66b.png

 在使用完开辟的内存,不再需要时,可以通过free来释放和回收内存

2.2 free

函数原型如图

9aa92001127c4ef480ce7fd888692ef5.png

free函数用来释放动态内存开辟的空间,但有几点需要注意

  •  参数ptr指向的内存必须是动态开辟的并且必须是开辟空间的起始地址,如果不是,那free函数的行为是未定义的
  • 如果参数ptr是NULL指针,那函数什么都不会做
  • 使用free函数时,需要包含头文件<stdlib.h>
  • 在使用free函数后,是把申请的内存空间还给操作系统,如果不把指针设为空指针,会使得指针成为野指针

933f251d53ed4498b6aceb717c7a4f7d.png

 三、calloc和realloc

3.1 calloc

函数原型如图

3fdb3593a9b5416a9ed9a2984037744f.png

  •  函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0 例如:num=5,size=4,表示开辟5个大小为4个字节的空间,即开辟大小20个字节的空间

6778bf2fdd39435b8f50a17dae937bf7.png

d621a67b888942058389da47f5b947ba.png

d7910745ced346679dd3af4e8fb05a0e.png 

图一为malloc开辟的空间,图二为calloc开辟的空间 通过观察可以发现,calloc开辟的空间会将每个字节初始化为0

 

  • 与malloc的区别只在于calloc会在返回地址前,把申请的内存空间每个字节初始化为0

41fae0ec6bc2485088b3ae6fcc232f85.png

 3.2 realloc

函数原型如图

f4ec3c97d8454795b3f133c8a0792dbb.png

 realloc函数可以对内存的大小做灵活的调整

  • ptr是要调整的内存地址,若ptr为空指针,功能与malloc等价
  • size是调整后的新大小
  • 返回值为调整之后的内存起始地址

529ee70cf1c54b778c3e55e5b5fa52c4.png

realloc扩容有三种情况

f50766b2a17d483f83dc88a7398b367e.png

 

情况一可直接扩容

情况二需要

1. 在堆区的内存中找到一个新的空间,并且满足大小要求

2. 把原来空间的数据拷贝一份到新的空间

3. 释放旧的空间

4. 返回新的内存空间的起始地址

情况三 若调整失败,返回NULL指针

ab048e74947649e096dc146ee395e509.png

四、常见的动态内存的错误

4.1 对NULL指针的解引用操作

7f5d7d351f244c748df2206bcda092e2.png

 

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

155fa698e0ad4970934db70bd4b32c47.png

 4.3 对非动态开辟内存进行释放

1e29dabe36aa4415bcc0282e63f5256d.png

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

7f8c2fe1fee44465a02859976fbcbed1.png

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

d1906c643a4640ea80f0c4394975e438.png

 4.6 动态开辟内存忘记释放(内存泄露)

e6ca3b30ce7c41b2a154029d8471bd24.png

五、柔性数组

C99中,在结构体中,最后一个成员是数组,且数组没有指定大小,这个数组才是柔性数组

39a9f389ad424b289b227fff6c0eb99d.png

 5.1 柔性数组的特点

  • 结构体中的柔性数组成员前面必须至少有一个其他成员
  • sizeof返回的这种结构体大小不包括柔性数组的内存
  • 包含柔性数组成员的结构体用malloc函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小

ca8ac8b0638e4a2e870e21f9bc52e9a5.png

 715cff4f14b749feb6b8536622b2167f.png

 70e2f074e6c34c059c35c29930a20b9e.png

ffae4393b4d44861ae9329fd94cd3a39.png 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值