【C语言进阶篇】动态内存管理

news2024/11/20 13:37:09

【C语言进阶篇】动态内存管理

🌈个人主页:开敲

🔥所属专栏:C语言

🌼文章目录🌼

1. 为什么要有动态内存分配

2.动态内存开辟和释放函数

   2.1 动态内存释放函数

      2.1.1 free函数

   2.2 动态内存开辟函数

      2.2.1 malloc函数 

      2.2.2 calloc函数

      2.2.3 realloc函数

3. 常见的动态内存的错误

    3.1 对NULL指针的解引用操作

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

    3.3 对非动态开辟的内存free释放

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

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

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

4. 动态内存经典笔试题练习

5. 柔性数组

    5.1 柔性数组的特点

    5.2 柔性数组的使用

    5.3 柔性数组的优势

1. 为什么要有动态内存分配

  在我们之前的学习中,掌握的开辟内存的方式有:

1 int a = 0;//在上开辟四个字节的空间

int arr[10] = {0};//在上开辟10个整型(40个字节)的空间

但是上面这种开辟空间的方法有两个特点:

① 开辟空间的大小是固定的

② 数组在声明的时候,一旦数组的大小确定下来了,在编译时就不能改变

  但在我们实际的情况中,对于空间的需求不仅仅是上述的情况,实际我们可能在编译的时候才能确定所需空间的大小,这个时候上面这写开辟空间的方式就不适用了。这个时候就需要用到我们灵活(动态)地进行内存地开辟。

  C语言引入了动态内存开辟,这使得程序员能够自己根据实际需求来申请释放空间,使代码更加的灵活。

  下面就来介绍几个动态内存开辟的函数和动态内存释放的函数。

2.动态内存开辟和释放函数

  这些函数都声明在头文件<stdlib.h>

   2.1 动态内存释放函数
      2.1.1 free函数

  free函数是动态内存管理中必不可少的十分重要的一个函数,用来将动态开辟的内存释放和回收,函数说明如下:

 ① 如果参数ptr所指向的空间不是动态开辟的,那么free的行为是未定义的

 ② 如果参数ptr是NULL指针,则函数什么事也不干

  函数使用:

   2.2 动态内存开辟函数
      2.2.1 malloc函数 

  这个函数向内存申请一块连续的可用地址,并且返回这块地址的指针。size(需要开辟的空间大小,单位是字节)下面是关于这个函数的一些说明:

   如果开辟成功,则返回一个指向开辟好的空间地址的指针

  如果开辟失败,则返回一个NULL指针。(因此,malloc的返回值必须进行检查)

  ③ 返回指针的类型为void*,因此malloc可以返回一个任意类型的指针,具体类型由使用者决定

  ④ 如果参数size为0,malloc执行的行为是未定义的,具体由编译器决定

  函数使用:

      2.2.2 calloc函数

  calloc函数与malloc函数十分相似,区别在于calloc函数的参数部分多了一个num(需要开辟的元素个数),size(每个元素的大小)并且calloc函数在开辟空间时会将开辟好的空间里的内容初始化为0:

 ​​​​​​​

      2.2.3 realloc函数

  在我们动态开辟内存时,时而会发现申请的空间太小了,时而又会发现申请的空间太大了,那么为了能够合理地使用内存,我们就需要对内存的大小进行调整。realloc函数就可以做到对动态开辟内存大小的调整。

  realloc函数的作用是:将动态开辟的空间扩容。

  ① ptr是要调整的内存的地址

  size是调整之后希望的大小

  ③ 返回的是调整之后的内存起始地址

  ④ 在调整好空间后,将原有空间内已经存放的数据拷贝到新的空间中

   ​​​​​​​realloc在调整内存空间大小时有以下两种情况:

    1. 原有空间后有足够大的空间存放扩容后的空间,直接向后开辟新的空间

    2. 原有空间后没有足够大的空间,则重新找一块可用的足够大的空间进行开辟,然后返回新空间的地址,同时将原有空间的数据拷贝到新的空间中

由于上述两种情况的存在,在使用realloc函数时必须对realloc返回的指针进行是否为NULL指针的检查。

3. 常见的动态内存的错误
  3.1 对NULL指针的解引用操作

  1  void text()

  2   {

  3        int* p = (int*)malloc(INT_MAX);//这里由于需要开辟的空间太大,导致无法正常开辟

  4                                                          //空间,malloc返回一个NULL指针

  5        *p = 20;//这里的p为NULL指针,对NULL指针解引用操作

  6         free(p);

  7   }

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

  void text()

2       {

3                int i = 0;

              int* pf = (int*)malloc(10*sizeof(int));

              if(pf==NULL)

6                     {

7                           exit(EIXT_FAILURE);

8                     }     

9                 for(i = 0;i<=10;i++)  //循环次数为11次 

10                   {

11                          *(p+i) = i;

12                   }

13                free(pf);

14                pf = NULL;

15       }          

    3.3 对非动态开辟的内存free释放

1    void text()

2       {

3             int a = 10;

4             int*p = &a;

5             free(p);

6       }

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

1    void text()

2       {

3              int* pf = (int*)malloc(5*sizeof(int));

4              pf++;    //pf不再指向动态开辟内存的起始地址

5              free(p);

6       }

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

1    void text()

2       {

3             int* pf = (int*)malloc(5*sizeof(int));

4             free(p);

5             free(p);

6       }

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

  这是动态内存错误中最严重的一个错误

1    void text()

2        {

3               int* pf = (int*)malloc(5*sizeof(int));

4               if(pf != NULL)

5                     {

6                           *p = 20;

7                      }

8        }

9

10    int main()

11       {

12            text();

13            while(1);//这里再出函数之后直接进入了死循环,函数内也没有释放内存,导致

14                           //开辟的这块空间无法释放,也无法使用,导致内存泄漏

15       }

切记:动态开辟的内存一定要释放,并且正确释放!

4. 动态内存经典笔试题练习

  题目1:

题目2:

题目3:

题目4:

5. 柔性数组

  也许你从来没有听说过柔性数组(flexible array)这个概念,但它确实是存在的。

  C99中,结构体中的最后一个元素是位置大小的数组,这个数组就是柔性数组。例如:

1    typedef struct st_sype

2       {

3              int i = 0;

4              int a[ ];

5       }type_a;

    5.1 柔性数组的特点

   结构体中的柔性数组成员前必须有至少一个其它成员(保证柔性数组是结构体的最后一个成员)

  ② sizeof在计算这种结构体时,不会算入柔性数组的大小

  ③ 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应当大于sizeof计算结构体的大小,以保证能够满足柔性数组的预期大小。

  例如:

​​​​​​​1       typedef struct st_type
2           {
3               int i;
4               int a[0];//柔性数组成员
5           }type_a;

6
7       int main()
8           {
9              printf("%d\n", sizeof(type_a));//输出的是4
10              return 0;
11          }
 

    5.2 柔性数组的使用

  1  //代码1
  2   #include <stdio.h>
  3   #include <stdlib.h>
  4     int main()
  5        int i = 0;
  6           type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
  7           //业务处理
  8            p->i = 100;
  9            for(i=0; i<100; i++)
  10               {
  11                    p->a[i] = i;
  12               }
  13           free(p);
  14           return 0;
  15        }
 

这样柔性数组成员a相当于获得了100个整型元素的连续空间。

  上述的type_a结构也可以设计为下面的结构,也能完成相同的效果。

//代码2
2     #include <stdio.h>
3     #include <stdlib.h>

4
5      typedef struct st_type
6             {
7                 int i;
8                 int *p_a;
9             }type_a;
10        int main()

11          {

12             type_a *p = (type_a *)malloc(sizeof(type_a));
13              p->i = 100;
14              p->p_a = (int *)malloc(p->i*sizeof(int));
15              //业务处理
16              for(i=0; i<100; i++)
17                   {
18                        p->p_a[i] = i;
19                   }
20               //释放空间
21               free(p->p_a);
22               p->p_a = NULL;
23               free(p);

24               p = NULL;
25               return 0;
26             }
 

    5.3 柔性数组的优势

  上述代码1代码2可以完成相同的功能,但是代码1有两个好处:

  ①:方便内存释放

  如果我们的代码是在⼀个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给用户⼀个结构体指针,用户做⼀次free就可以把所有的内存也给释放掉。

  ②:有利于访问速度

  连续的内存有益于提高访问速度以及提高内存的利用率,也有益于减少内存碎片,提高内存的利用率。

                                                  创作不易,点个赞呗,谢谢啦~

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1539700.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

prettier + eslint 配置

vue-cli 新建项目选择 ESLint Prettier 会自动下载相关包 settings.json {"editor.formatOnSave": true, // 开启保存文件自动格式化代码"editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的代码格式化工具// "prettier.r…

二.寄存器

1. 2. 例如&#xff1a;h即为high&#xff08;高位&#xff09;&#xff0c;l即为low&#xff08;低位&#xff09; 3.一个字是两个字节 4.在写一条汇编指令或一个寄存器的名称时不区分大小写。 5.al&#xff0c;ah&#xff0c;ax在接受汇编指令时&#xff0c;并不相等&…

混合云构建-使用 Azure ExpressRoute 建立从本地到 Azure 虚拟网络的专用连接

如果有大量业务数据需要在本地数据中心和azure私有网络进行传输&#xff0c;同时保证带宽和时延的情况需要使用 ExpressRoute 设置从本地网络到 Azure 中的虚拟网络的专用连接。以下是实操步骤供参考&#xff1a; 一、创建和预配 ExpressRoute 线路 登录 Azure 门户。 在页面…

React系列 之 React进阶 含源码解读 (一)事件合成、state原理

资料来源&#xff1a;掘金课程 https://juejin.cn/book/6945998773818490884?enter_fromcourse_center&utm_sourcecourse_center 记录一些笔记 事件合成 React的事件其实是React重新实现的一套事件系统。目标是统一管理事件&#xff0c;提供一种跨浏览器一致性的事件处…

3、Jenkins持续集成-Jenkins安装和插件管理

文章目录 一、Jenkins安装1. 安装JDK2. 获取jenkins安装包3. 安装包上传到服务器&#xff0c;进行安装4. 修改Jenkins配置&#xff08;1&#xff09;低版本Jenkins的rpm包&#xff08;2&#xff09;高版本Jenkins的rpm包 5. 启动Jenkins6. 打开浏览器访问7. 获取并输入admin账户…

Linux文件 profile、bashrc、bash_profile区别

Linux系统中&#xff0c;有三种文件 出现的非常频繁&#xff0c;那就是 profile、bash_profile、bashrc 文件。 1、profile 作用 profile&#xff0c;路径&#xff1a;/etc/profile&#xff0c;用于设置系统级的环境变量和启动程序&#xff0c;在这个文件下配置会对所有用户…

pytest运行结果解析及其改造

简介&#xff1a;场景假设 - 当运行pytest完成后&#xff0c;需要针对运行的结果进行即时的反馈&#xff0c;打印 PASS 或者 FAIL&#xff0c;及其运行失败的原因&#xff0c;最后将结果推送给消息机器人。 历史攻略&#xff1a; pytestallure安装和使用 pytest&#xff1a;…

AcWing 1250. 格子游戏 (并查集,坐标变换)

记录此题的目的&#xff1a; 明确二维的坐标可以映射到一维&#xff1a;在x和y都是从0开始的前提下&#xff0c;假如图形列数为n&#xff0c;(x,y)映射到一维可以写成x * n y。并查集并不好存储二维数据&#xff0c;如果遇到二维数据可以将其映射到一维。 Alice和Bob玩了一个…

git 上传文件夹至远端仓库的方法

上传的远端git可以是gitlab、github、gitee、gitblit或者gitCode等等 以下以GitHub为例说明&#xff1a; 1、登录GitHub网站&#xff08;账户/密码&#xff09; 2、创建一个新的空白项目&#xff08;或者已有的项目&#xff09;hello-world 分支是master &#xff0c;这里默认即…

el-tab 如何点击不同标签触发不同函数

介绍 el-tab本身的功能是点击之后切换不同页&#xff0c;但是我希望点击不同标签就触发不同页 代码实现 <template><el-tabsv-model"activeName"type"card"class"demo-tabs"tab-click"handleClick"><el-tab-pane lab…

PTA L2-028 秀恩爱分得快

古人云&#xff1a;秀恩爱&#xff0c;分得快。 互联网上每天都有大量人发布大量照片&#xff0c;我们通过分析这些照片&#xff0c;可以分析人与人之间的亲密度。如果一张照片上出现了 K 个人&#xff0c;这些人两两间的亲密度就被定义为 1/K。任意两个人如果同时出现在若干张…

dash 初体验(拔草)

Dash简介 Dash 是一个高效简洁的 Python 框架&#xff0c;建立在 Flask、Poltly.js 以及 React.js 的基础上&#xff0c;设计之初是为了帮助前端知识匮乏的数据分析人员&#xff0c;以纯 Python 编程的方式快速开发出交互式的数据可视化 web 应用。 搭建环境 在学习 Dash 的…

深入理解Netty以及为什么项目中要使用?(七)Netty中ByteBuf详解

在Netty中&#xff0c;还有另外一个比较常见的对象ByteBuf&#xff0c;它其实等同于Java Nio中的ByteBuffer&#xff0c;但是ByteBuf对Nio中的ByteBuffer的功能做了很作增强&#xff0c;下面我们来简单了解一下ByteBuf。 下面这段代码演示了ByteBuf的创建以及内容的打印&#…

#Idea打包诺依 多模块项目遇到的问题

##诺依框架中遇到的问题 1. 打包部署出错 Please refer to /Users/zhang/code/giteeProjects/wms-ruoyi/ruoyi-generator/target/surefire-reports for the individual test results. Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [dat…

【技术栈】Redis 删除策略

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8; 友情提供 目录 相关传送门 前言 1. 删除策略的目标 2. 数据删除策略 2.1 定时删除 2.2 惰性删除 2.3 定期删除…

智能财务新选择!Zoho Books入选福布斯榜单,助力中小企业!

放眼全球&#xff0c;中小企业始终是经济发展的重要组成部分。然而&#xff0c;由于中小企业的规模、流程规范和资源等方面受限较多&#xff0c;从而导致其在管理及运营上存在着诸多问题。其中包括财务管理不规范、成本控制不到位、运营效率低下等&#xff0c;这些问题则直接影…

freeRTOS动态内存heap4源码分析

1 前言 随着功能安全的推广&#xff0c;动态内存分配在RTOS领域的用武之地将越来越小。但heap4毕竟是为RTOS量身打造&#xff0c;相对简单&#xff0c;作为堆内存管理的入门学习&#xff0c;仍是很不错的选择。 1.1 标准c库动态内存函数的弊端 对于标准C库的malloc和free函数&…

2024年【安全员-A证】免费试题及安全员-A证作业考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【安全员-A证】免费试题及安全员-A证作业考试题库&#xff0c;包含安全员-A证免费试题答案和解析及安全员-A证作业考试题库练习。安全生产模拟考试一点通结合国家安全员-A证考试最新大纲及安全员-A证考试真题汇…

动态规划——斐波那契问题(Java)

目录 什么是动态规划&#xff1f; 练习 练习1&#xff1a;斐波那契数 练习2&#xff1a;三步问题 练习3&#xff1a;使用最小花费爬楼梯 练习4&#xff1a;解码方法 什么是动态规划&#xff1f; 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;&…

8个国产全能型AI写作神器,给个标题就能自动生成全文 #其他#知识分享

国外ChatGPT爆火&#xff0c;AI写作在国内也引起不小的瞩目&#xff0c;目前国内的AI写作工具少说也有几十上百个&#xff0c;要在这么多AI写作中找出适合自己的工具&#xff0c;一个一个尝试是不太现实的&#xff0c;所以今天就给大家推荐一些款AI写作工具。帮助你少走弯路&am…