【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,一经查实,立即删除!

相关文章

DataFrame:postgresql数据下载及上传

import pandas as pd import numpy as np import re import psycopg2 from sqlalchemy import create_engine1. 连接数据库&#xff0c;下载所需数据 def download_sqlfile(sql_file):"""连接数据库&#xff0c;下载所需数据"""# 建立数据库连接…

养成类游戏为何具有吸引力及其心理效应探究

在当今数字化时代&#xff0c;养成类游戏以其独特的魅力吸引了大量玩家。这类游戏的核心玩法在于模拟现实中的成长过程&#xff0c;如养育宠物、建设家园、培养角色等&#xff0c;使玩家投入时间和精力去照顾和培育虚拟实体的发展。本文旨在探讨养成类游戏带给玩家的快感来源及…

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

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

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

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

nginx rewrite重写

Nginx 的 rewrite 指令可以用来修改 URI的请求。通过 rewrite&#xff0c;你可以重定向 URL 请求、修改 URL 参数、或者根据特定条件进行URL跳转 等操作。Nginx 重写规则用于更改客户端请求的全部或部分URL。更改URL的主要目的是通知客户端他们正在查找的资源已经更改了位置。N…

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

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

使用egg.js发送jwt

下载jwt 配置jwt pnpm i egg-jwt plugin.js /** type Egg.EggPlugin */ module.exports {jwt:{enable:true,package:egg-jwt} }; config.default.js config.jwt {secret:"hakurei77" //密钥} 创建中间层 app -> middleware //检查token module.…

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;支持自定义页面。 源码 …

Python XML 解析

在Python中&#xff0c;解析XML文档通常使用ElementTree模块。ElementTree提供了一种简单的方式来解析和操作XML数据。下面通过一个案例来说明如何使用Python的ElementTree来解析XML文档&#xff1a; 假设有一个名为books.xml的XML文件&#xff0c;内容如下&#xff1a; xml &…

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

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

桥接模式简介

在Java中&#xff0c;桥接模式&#xff08;Bridge Pattern&#xff09;的核心思想是将抽象部分与其实现部分分离&#xff0c;使其可以独立变化。以下是桥接模式的一个简单示例&#xff0c;我们将创建一个图形渲染系统&#xff0c;该系统允许用户选择不同的形状&#xff08;抽象…

redis-大key优化

什么是大key Redis 中的“大 key”问题指的是单个键&#xff08;key&#xff09;所存储的值&#xff08;value&#xff09;过大&#xff0c;导致影响 Redis 的性能和稳定性。通常情况下&#xff0c;Redis 的设计初衷是将数据存储在内存中&#xff0c;因此单个键的大小应该受到…

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

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

Java基础【异常】

异常是什么 异常是指程序执行过程中可能出现的错误或意外情况。 异常有哪些种类 编译异常 Checked Exception&#xff1a;这类异常在代码编译期间就被检测出来&#xff0c;必须显式地进行处理&#xff0c;否则编译不通过。常见的Checked Exception包括IOException、SQLExcept…

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…

深度解析大模型的关键特性与优势

大模型通常指的是具有极其庞大参数规模、先进架构和广泛适用性的机器学习模型。这类模型因其前所未有的规模和强大的通用性而备受关注&#xff0c;下面是大模型的一些显著特点&#xff1a; 大规模参数量&#xff1a; 大模型的核心特征之一就是其庞大的参数规模&#xff0c;动辄…

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

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

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

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