当前位置: 首页 > news >正文

学C的第三十二天【动态内存管理】

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

学C的第三十一天【通讯录的实现】_高高的胖子的博客-CSDN博客

 =========================================================================

                     

1 . 为什么存在动态内存分配

学到现在认识的内存开辟方式有两种:

              

  • 创建变量:       

int val = 20;        ——        在栈空间上开辟4个字节

  • 创建数组:       

char arr[10] = {10};        ——        在栈空间上开辟10个字节的连续空间

                     

                     

                     

上述的开辟空间的方式有两个特点:

               

  • 空间开辟大小固定的。
  • 数组声明候,必须指定数组的长度,它所需要的内存在编译时分配

                   

但是对于空间的需求不仅仅是上述的情况

有时候我们需要的空间大小在程序运行的时候才能知道

数组的编译时开辟空间的方式就不能满足了。

这时候就只能试试动态内存开辟了。

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 动态内存函数的介绍

(1). malloc 和 free :

                 

malloc :

该函数是一个动态内存开辟的函数

这个函数可以向内存申请一块连续可用的空间

返回指向这块空间的指针

               

书写格式如下

void* malloc (size_t size); 

                

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

                

  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查

                  

  • 返回值的类型void* ,所以malloc函数并不知道开辟空间的类型
  • 具体在使用的时候使用者自己决定

               

  • 如果参数 size 为0malloc行为是标准未定义的,取决于编译器

             

  • malloc声明在 stdlib.h 头文件中。

示例:

                       

free :

malloc函数申请的内存空间程序退出时才会还给操作系统

如果程序不退出,动态申请的内存不会主动释放的。

所以需要 free函数释放动态内存

               

书写格式如下

void free (void* ptr);

                

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

        

  • 如果参数 ptr 是NULL指针,则函数什么事都不做

           

  • free声明在 stdlib.h 头文件中。

示例:

                     


                    

(2). calloc :

         

书写格式如下

void* calloc (size_t num, size_t size);

                

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

               

  • 如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。

               

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

              

  • calloc 声明在 stdlib.h 头文件中。

示例:

                     


                    

(3). realloc :

         

realloc函数的出现让动态内存管理更加灵活

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,

那为了合理地使用内存,我们一定会对内存的大小做灵活的调整

realloc 函数就可以做到对动态开辟内存大小的调整

                                

书写格式如下

void* realloc (void* ptr, size_t size);

                                  

  • ptr 要调整的内存地址,如果填的是NULL空指针,那会开辟一块新的空间跟malloc函数一样

                  

  • size 调整之后新大小

                  

  • 返回值 调整之后的内存起始位置

                  

  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

                  

  • realloc在调整内存空间的是存在两种情况

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

在这种情况下,要扩展内存直接在原有内存之后直接追加空间

原来空间的数据不发生变化

                           

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

在这种情况下,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用

旧的空间中的数据拷贝到新的空间中,再释放旧的空间,最后返回新空间的起始地址

这样函数返回的就是一个新的内存地址

                  

  • realloc 声明在 stdlib.h 头文件中。

示例:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

3 . 常见的动态内存错误

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

                      

malloccallocrealloc函数 可能开辟空间
    开辟空间就有可能会失败返回 NULL空指针
    这时解引用该空指针就可能会出问题

示例:

                     


                    

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

示例:

                     


                    

(3). 对非动态开辟内存使用free函数释放:

示例:

                     


                    

(4). 使用free函数释放一块动态开辟内存的一部分:

                      

使用动态空间过程中

改变指向动态空间的指针

这时要使用free函数释放空间就会出问题

示例:

                     


                    

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

                      

可以在释放动态空间后

将该空间指针设置为空指针

防止多次释放

示例:

                     


                    

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

                      

只有两种方式可以对动态内存进行释放

free函数 程序运行结束

所以如果 忘记释放  没释放且程序无法结束

就会造成内存泄漏

示例:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

4 . 相关经典笔试题

题一:

                 

进行修改:

               

对应代码:

//1:改前
#include <stdio.h>
#include <stdlib.h>
#include <string.h>void GetMemory(char* p)
{//开辟动态空间p = (char*)malloc(100);
}void Test(void)
{//创建空指针:char* str = NULL;//使用该指针进行动态内存开辟:GetMemory(str);//对动态空间赋值并使用:strcpy(str, "hello world");printf(str);}int main()
{Test();return 0;
}//1:改后
#include <stdio.h>
#include <stdlib.h>
#include <string.h>void GetMemory(char** p)
{//开辟动态空间*p = (char*)malloc(100);
}void Test(void)
{//创建空指针:char* str = NULL;//使用该指针进行动态内存开辟:GetMemory(&str);//对动态空间赋值并使用:strcpy(str, "hello world");printf(str);//使用后进行释放:free(str);str = NULL;
}int main()
{Test();return 0;
}

                     


                    

题二:

               

进行修改:

               

对应代码:

//2:改前
#include <stdio.h>
#include <stdlib.h>char* GetMemory(void)
{char p[] = "hello world";return p;
}void Test(void)
{//创建空指针:char* str = NULL;//调用上面的函数:str = GetMemory();printf(str);
}int main()
{Test();return 0;
}//2:改后
#include <stdio.h>
#include <stdlib.h>char* GetMemory(void)
{static char p[] = "hello world";return p;
}void Test(void)
{//创建空指针:char* str = NULL;str = GetMemory();printf(str);
}int main()
{Test();return 0;
}

                     


                    

题三:

             

进行修改:

               

对应代码:

//3:改前:
#include <stdio.h>
#include <stdlib.h>void GetMemory(char** p, int num)
{//根据需求创建动态空间:*p = (char*)malloc(num);
}void Test(void)
{//创建空指针变量:char* str = NULL;//调用函数:GetMemory(&str, 100);//使用动态空间:strcpy(str, "hello");printf(str);
}int main()
{Test();return 0;
}//3:改后:
#include <stdio.h>
#include <stdlib.h>void GetMemory(char** p, int num)
{//根据需求创建动态空间:*p = (char*)malloc(num);
}void Test(void)
{//创建空指针变量:char* str = NULL;//调用函数:GetMemory(&str, 100);//使用动态空间:strcpy(str, "hello");printf(str);//释放:free(str);str = NULL;
}int main()
{Test();return 0;
}

                     


                    

题四:

                

进行修改:

               

对应代码:

//4:改前:
#include <stdio.h>
#include <stdlib.h>void Test(void)
{//创建动态空间并接收:char* str = (char*)malloc(100);//使用动态空间:strcpy(str, "hello");//释放:free(str);if (str != NULL){strcpy(str, "world");printf(str);}
}int main()
{Test();return 0;
}//4:改后:
#include <stdio.h>
#include <stdlib.h>void Test(void)
{//创建动态空间并接收:char* str = (char*)malloc(100);//使用动态空间:strcpy(str, "hello");//释放:free(str);str = NULL;if (str != NULL){strcpy(str, "world");printf(str);}
}int main()
{Test();return 0;
}

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

5 . C/C++程序的内存开辟

(1). C/C++程序内存区域划分:

                

C/C++程序内存分配的几个区域:

1. 栈区(stack):

执行函数时函数内局部变量的存储单元都可以在栈上创建

函数执行结束时这些存储单元自动被释放

栈内存分配运算内置于处理器的指令集中效率很高,但是分配的内存容量有限

栈区主要存放运行函数而分配的局部变量函数参数返回数据返回地址等。

2. 堆区(heap):

一般由程序员分配释放若程序员不释放

程序结束时可能由OS(操作系统)回收

分配方式类似于链表

3. 数据段(静态区)(static)

存放全局变量静态数据程序结束后由系统释放

4. 代码段:

存放函数体类成员函数和全局函数)的二进制代码

           

图示:

          

 有了这幅图,

我们就可以更好的理解static关键字修饰局部变量的例子了。

实际上普通的局部变量在栈区分配空间的,

栈区的特点在上面创建的变量出了作用域就销毁

但是被static修饰的变量存放在数据段(静态区)

数据段的特点是在上面创建的变量直到程序结束才销毁

所以生命周期变长

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

6 . 柔性数组

             

C99中,结构体中最后一个元素允许是未知大小的数组,这就叫做柔性数组成员

              

实例:

                  

(1). 柔性数组的特点:

                    

  • 结构体中的柔性数组成员前面必须至少有一个其他成员

  • sizeof 返回的这种结构体大小不包括柔性数组的内存

  • 包含柔性数组成员的结构体malloc ()函数进行内存的动态分配
    并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

              

实例:

                     


                    

(2). 柔性数组的使用:

              

实例:

                     


                    

(3). 柔性数组的优势:

方便内存释放

如果我们的代码是在一个给别人用的函数中

在里面做了二次内存分配(使用两次malloc函数可以实现类似柔性数组的效果)

把整个结构体返回给用户

用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free

所以你不能指望用户来发现这个事

所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了

返回给用户一个结构体指针

用户做一次free就可以把所有的内存也给释放掉

             

有利于访问速度

连续的内存益于提高访问速度

也有益于减少内存碎片(两个开辟的空间中间空余的内存)

相关文章:

学C的第三十二天【动态内存管理】

相关代码gitee自取&#xff1a;C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 学C的第三十一天【通讯录的实现】_高高的胖子的博客-CSDN博客 1 . 为什么存在动态内存分配 学到现在认识的内存开辟方式有两种&#xff1a; 创建变量&#xff1a; int val …...

聊聊elasticsearch的data-streams

序 本文主要研究一下elasticsearch的data-streams data-streams 主要特性 首先data streams是由一个或者多个自动生成的隐藏索引组成的&#xff0c;它的格式为.ds-<data-stream>-<yyyy.MM.dd>-<generation> 示例.ds-web-server-logs-2099.03.07-000034&a…...

unreal engine c++ 创建tcp server, tcp client

TCP客户端 TcpConnect.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "Common/UdpSocketReceiver.h" #include "GameFramework/Actor.h"DECLARE_DELEGATE…...

24届华东理工大学近5年自动化考研院校分析

今天给大家带来的是华东理工大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、华东理工大学 学校简介 华东理工大学原名华东化工学院&#xff0c;1956年被定为全国首批招收研究生的学校之一&#xff0c;1960年起被中共中央确定为教育部直属的全国重点大学&#…...

初识集合和背后的数据结构

目录 集合 Java集合框架 数据结构 算法 集合 集合&#xff0c;是用来存放数据的容器。其主要表现为将多个元素置于一个单元中&#xff0c;用于对这些元素进行增删查改。例如&#xff0c;一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合&#xff09;。 Java中有很多种集…...

选择适合你的数据可视化工具:提升洞察力的关键决策

导言&#xff1a; 在当今数据驱动的世界中&#xff0c;数据可视化工具成为了帮助我们理解和传达数据见解的关键工具之一。数据可视化不仅能够将复杂的数据转化为易于理解的可视化形式&#xff0c;还能帮助我们发现数据中的模式、趋势和关联。然而&#xff0c;随着市场上可视化工…...

H5中的draggable

基本语法及事件 draggable 属性规定元素是否可拖动。必须设置&#xff0c;否则没有拖拽效果及事件触发 提示&#xff1a; 链接和图像默认是可拖动的。 提示&#xff1a; draggable 属性经常用于拖放操作 语法 <element draggable"true|false|auto"> 值描…...

搭建SVN服务器

简介 SVN&#xff08;Subversion&#xff09;是一种版本控制工具&#xff0c;用于管理和跟踪文件的修改历史。它可以帮助团队协作开发&#xff0c;方便地共享和更新代码&#xff0c;同时也可以提供备份和安全控制功能。 使用SVN&#xff0c;你可以创建中央代码库&#xff08;…...

OpenCV之信用卡识别实战

文章目录 代码视频讲解模板匹配文件主程序(ocr_template_match.py)myutils.py 代码 链接: https://pan.baidu.com/s/1KjdiqkyYGfHk97wwgF-j3g?pwdhhkf 提取码: hhkf 视频讲解 链接: https://pan.baidu.com/s/1PZ6w5NcSOuKusBTNa3Ng2g?pwd79wr 提取码: 79wr 模板匹配文件 …...

Detector定位算法在FPGA中的实现——section1 原理推导

关于算法在FPGA中的实现,本次利用业余的时间推出一个系列章节,专门记录从算法的推导、Matlab的实现、FPGA的移植开发与仿真做一次完整的FPGA算法开发,在此做一下相关的记录和总结,做到温故知新。 这里以Detector在Global Coordinate System(原点为O)中运动为背景,Detect…...

心电信号去噪:方法与应用

目录 1 去噪技术的发展历程 2 滤波器去噪的应用 3 小波去噪的优势 4 深度学习去噪的前景...

睡眠助手/白噪音/助眠夜曲微信小程序源码下载 附教程

睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程 支持分享海报 支持暗黑模式 包含了音频数据 最近很火的助眠小程序&#xff0c;前端vue&#xff0c;可以打包H5&#xff0c;APP&#xff0c;小程序 后台可以设置流量主广告&#xff0c;非常不错的源码 代码完整 完美运营 搭配无…...

Spring Cloud常见问题处理和代码分析

目录 1. 问题&#xff1a;如何在 Spring Cloud 中实现服务注册和发现&#xff1f;2. 问题&#xff1a;如何在 Spring Cloud 中实现分布式配置&#xff1f;3. 问题&#xff1a;如何在 Spring Cloud 中实现服务间的调用&#xff1f;4. 问题&#xff1a;如何在 Spring Cloud 中实现…...

debian怎么修改man help为中文,wsl怎么修改显示语言为中文

在Debian 12系统中&#xff0c;要将系统语言和Man帮助手册设置为中文&#xff0c;需要执行以下步骤&#xff1a; 安装中文语言包&#xff1a; 首先&#xff0c;更新软件包列表并安装中文语言包。打开终端并运行以下命令&#xff1a; sudo apt update sudo apt install locales配…...

k8s概念-亲和力与反亲和力

回到目录 亲和力 Affinity 对部署调度时的优先选择 分为 节点亲和力 pod亲和力 pod反亲和力 节点亲和力 NodeAffinity 进行 pod 调度时&#xff0c;优先调度到符合条件的亲和力节点上 可配置 硬亲和力和软亲和力 RequiredDuringSchedulingIgnoredDuringExecution 硬…...

【数据结构】实现单链表的增删查

目录 1.定义接口2.无头单链表实现接口2.1 头插addFirst2.2 尾插add2.3 删除元素remove2.4 修改元素set2.5 获取元素get 3.带头单链表实现接口3.1 头插addFirst3.2 尾插add3.3 删除元素remove3.4 判断是否包含元素element 1.定义接口 public interface SeqList<E>{//默认…...

Vue2 第二十节 vue-router (四)

1.全局前置路由和后置路由 2.独享路由守卫 3.组件内路由守卫 4.路由器的两种工作模式 路由 作用&#xff1a;对路由进行权限控制 分类&#xff1a;全局守卫&#xff0c;独享守卫&#xff0c;组件内守卫 一.全局前置路由和后置路由 ① 前置路由守卫&#xff1a;每次路由…...

第三章 图论 No.1单源最短路及其综合应用

文章目录 1129. 热浪1128. 信使1127. 香甜的黄油1126. 最小花费920. 最优乘车903. 昂贵的聘礼1135. 新年好340. 通信线路342. 道路与航线341. 最优贸易 做乘法的最短路时&#xff0c;若权值>0&#xff0c;只能用spfa来做&#xff0c;相等于加法中的负权边 1129. 热浪 1129.…...

❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件

❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件 cmd或者终端用nvm 安装提示&#xff1a; npm不是内部或外部命令,也不是可运行的程序或批处理文件 原因&#xff08;一&#xff09; 提示这个问题&#xff0c;有可能是Node没有安装&#xff0c;也有可能是没有配置…...

关于Godot游戏引擎制作流水灯

先上核心代码 游戏节点 流水灯的通途可以是 1. 装饰 2. 音乐类多媒体程序&#xff08;如FL中TB-303的步进灯&#xff09; FL Studio Transistor Bass...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...