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

【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.mzph.cn/news/765851.shtml

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

相关文章

【鸿蒙HarmonyOS开发笔记】应用数据持久化之通过用户首选项实现数据持久化

概述 应用数据持久化&#xff0c;是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象&#xff0c;存储介质上的数据形态可能是文本、数据库、二进制文件等。 HarmonyOS标准系统支持典型的存储数据形态&#xff0c;包…

OceanPen Art AI绘画系统 运营教程(三)2.10绘画全新界面升级

在一个崇高的目标支持下&#xff0c;不停地工作&#xff0c;即使慢&#xff0c;也一定会获得成功。 —— 爱因斯坦 演示站点&#xff1a; ai.oceanpen.art 官方论坛&#xff1a; www.jingyuai.com 一、前端用户界面全新体验 二、 MJ绘画分享 提示词自取&#xff1a;htt…

如何使用 ArcGIS Pro 制作好看的高程渲染图

虽然 ArcGIS Pro 已经提供了很多好看的配色方案&#xff0c;但是如果直接对高程DEM进行渲染效果不是很理想&#xff0c;我们可以结合山体阴影让高程渲染图看起来更加立体&#xff0c;这里为大家介绍一下制作方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是…

component-右侧抽屉组件

1.右侧抽屉组件 点击筛选&#xff0c;右侧抽屉滑出&#xff0c;点击取消或者点击空白处抽屉收起。 2.代码 <template><div class"all" click"hidden()"><!-- 抽屉 --><div class"drawer"><div class"drawerBo…

Android StateLayout状态页

文章目录 Android StateLayout状态页概述源码使用源码下载 Android StateLayout状态页 概述 StateLayout&#xff08;状态页&#xff09;包含&#xff1a;加载中页面&#xff0c;错误页面&#xff0c;空页面&#xff0c;内含状态默认页面&#xff0c;支持自定义页面。 源码 …

yolov8目标检测数据集制作——make sense

背景 在前几天我进行了录制视频以准备足够多的数据集&#xff0c;同时使用利用python自定义间隔帧数获取视频转存为图片&#xff0c;所以今天我准备对我要训练的数据集进行标注。我要做的是一个基于yolo的检测项目&#xff0c;在搜索资料后得知大家多是用labelme或者make sens…

使用 VS Code + Github 搭建个人博客

搭建个人博客的方案 现在&#xff0c;搭建个人博客的方式有很多&#xff0c;门槛也很低。 可以选择已有平台&#xff1a; 掘金语雀知乎简书博客园SegmentFault… 也可以选择一些主流的博客框架&#xff0c;自行搭建。 HexoGitBookVuePressdumi… 如何选择&#xff1f; 我…

Chain of Note-CoN增强检索增强型语言模型的鲁棒性

Enhancing Robustness in Retrieval-Augmented Language Models 检索增强型语言模型&#xff08;RALMs&#xff09;在大型语言模型的能力上取得了重大进步&#xff0c;特别是在利用外部知识源减少事实性幻觉方面。然而&#xff0c;检索到的信息的可靠性并不总是有保证的。检索…

[ESP32]:基于HTTP实现百度AI识图

[ESP32]&#xff1a;基于HTTP实现百度AI识图 测试环境&#xff1a; esp32-s3esp idf 5.1 首先&#xff0c;先配置sdk&#xff0c;可以写入到sdkconfig.defaults CONFIG_IDF_TARGET"esp32s3" CONFIG_IDF_TARGET_ESP32S3yCONFIG_PARTITION_TABLE_CUSTOMy CONFIG_PA…

值迭代和策略迭代【强化学习】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 文章目录 强化学习笔记一、Value It…

江苏开放大学2024年春《中级会计实务(上) 050284》第1次任务第一单元总论、第二单元存货练习参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 电大搜题 多的用不完的题库&#xff…

Qt教程 — 3.6 深入了解Qt 控件:Display Widgets部件(2)

目录 1 Display Widgets简介 2 如何使用Display Widgets部件 2.1 QTextBrowser组件-简单的文本浏览器 ​2.2 QGraphicsView组件-简单的图像浏览器 Display Widgets将分为两篇文章介绍 文章1&#xff08;Qt教程 — 3.5 深入了解Qt 控件&#xff1a;Display Widgets部件-CSDN…

Magic Copy:一键AI抠图,在浏览器中获得任何图像素材

Magic Copy&#xff1a;轻松一点&#xff0c;精准抠图&#xff0c;让创意无限放大&#xff01; - 精选真开源&#xff0c;释放新价值。 概览 Magic Copy&#xff08;AI智能抠图插件&#xff09;是一个创新型的浏览器扩展工具&#xff0c;其独特之处在于能够无缝集成于用户的网…

CCDP.02.OS正确部署后的Dashboard摘图说明

前言 在部署成功OpenStack后&#xff0c;应该可以在浏览器打开Dashboard&#xff0c;并对计算资源&#xff08;这里主要是指VM&#xff09;进行管理&#xff0c;也可以在Dashboard上面查看OpenStack是否存在错误&#xff0c;下面&#xff0c;已针对检查的关键点&#xff0c;用红…

Mysql之索引存储原理

在介绍索引实现之前&#xff0c;我们先来了解下几种树的数据结构&#xff1a; 一、二叉搜索树 二叉搜索树有以下性质&#xff1a; 1.每个节点有一个关键字 2.左右孩子至多有一个。 3.关键字大于左孩子&#xff0c;小于右孩子。 正因为二叉搜索树的特性&#xff0c;所以这种数…

基于java+springboot+vue实现的游戏账号估价交易平台(文末源码+Lw+ppt)23-555

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对游戏账号估价交易的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自…

Structured Knowledge Distillation for Accurate and Efficient Object Detection

摘要 许多之前的知识蒸馏方法是为图像分类而设计的&#xff0c;在具有挑战性的任务&#xff08;如目标检测&#xff09;中失败。本文首先提出了知识蒸馏在目标检测中失败的主要原因是&#xff1a;&#xff08;1&#xff09;前景和背景之间不平衡&#xff1a;(2)缺乏对不同像素…

PTA L2-027 名人堂与代金券

对于在中国大学MOOC&#xff08;http://www.icourse163.org/ &#xff09;学习“数据结构”课程的学生&#xff0c;想要获得一张合格证书&#xff0c;总评成绩必须达到 60 分及以上&#xff0c;并且有另加福利&#xff1a;总评分在 [G, 100] 区间内者&#xff0c;可以得到 50 元…

公司内部局域网怎么适用飞书?

随着数字化办公的普及&#xff0c;企业对于内部沟通和文件传输的需求日益增长。飞书作为一款集成了即时通讯、云文档、日程管理、视频会议等多种功能的智能协作平台&#xff0c;已经成为许多企业提高工作效率的首选工具。本文将详细介绍如何在公司内部局域网中应用飞书&#xf…

【机器学习之---统计】统计学基础概念

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 统计学基础 1. 频率派 频率学派&#xff08;传统学派&#xff09;认为样本信息来自总体&#xff0c;通过对样本信息的研究可以合理地推断和估计总体信息…