学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 为0,malloc的行为是标准是未定义的,取决于编译器。
- 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指针的解引用操作:
malloc、calloc、realloc函数 都可能开辟空间,
开辟空间就有可能会失败,返回 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自取:C语言学习日记: 加油努力 (gitee.com) 接上期: 学C的第三十一天【通讯录的实现】_高高的胖子的博客-CSDN博客 1 . 为什么存在动态内存分配 学到现在认识的内存开辟方式有两种: 创建变量: int val …...
聊聊elasticsearch的data-streams
序 本文主要研究一下elasticsearch的data-streams data-streams 主要特性 首先data streams是由一个或者多个自动生成的隐藏索引组成的,它的格式为.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年自动化考研院校分析
今天给大家带来的是华东理工大学控制考研分析 满满干货~还不快快点赞收藏 一、华东理工大学 学校简介 华东理工大学原名华东化工学院,1956年被定为全国首批招收研究生的学校之一,1960年起被中共中央确定为教育部直属的全国重点大学&#…...
初识集合和背后的数据结构
目录 集合 Java集合框架 数据结构 算法 集合 集合,是用来存放数据的容器。其主要表现为将多个元素置于一个单元中,用于对这些元素进行增删查改。例如,一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)。 Java中有很多种集…...
选择适合你的数据可视化工具:提升洞察力的关键决策
导言: 在当今数据驱动的世界中,数据可视化工具成为了帮助我们理解和传达数据见解的关键工具之一。数据可视化不仅能够将复杂的数据转化为易于理解的可视化形式,还能帮助我们发现数据中的模式、趋势和关联。然而,随着市场上可视化工…...
H5中的draggable
基本语法及事件 draggable 属性规定元素是否可拖动。必须设置,否则没有拖拽效果及事件触发 提示: 链接和图像默认是可拖动的。 提示: draggable 属性经常用于拖放操作 语法 <element draggable"true|false|auto"> 值描…...
搭建SVN服务器
简介 SVN(Subversion)是一种版本控制工具,用于管理和跟踪文件的修改历史。它可以帮助团队协作开发,方便地共享和更新代码,同时也可以提供备份和安全控制功能。 使用SVN,你可以创建中央代码库(…...
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 深度学习去噪的前景...
睡眠助手/白噪音/助眠夜曲微信小程序源码下载 附教程
睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程 支持分享海报 支持暗黑模式 包含了音频数据 最近很火的助眠小程序,前端vue,可以打包H5,APP,小程序 后台可以设置流量主广告,非常不错的源码 代码完整 完美运营 搭配无…...
Spring Cloud常见问题处理和代码分析
目录 1. 问题:如何在 Spring Cloud 中实现服务注册和发现?2. 问题:如何在 Spring Cloud 中实现分布式配置?3. 问题:如何在 Spring Cloud 中实现服务间的调用?4. 问题:如何在 Spring Cloud 中实现…...
debian怎么修改man help为中文,wsl怎么修改显示语言为中文
在Debian 12系统中,要将系统语言和Man帮助手册设置为中文,需要执行以下步骤: 安装中文语言包: 首先,更新软件包列表并安装中文语言包。打开终端并运行以下命令: sudo apt update sudo apt install locales配…...
k8s概念-亲和力与反亲和力
回到目录 亲和力 Affinity 对部署调度时的优先选择 分为 节点亲和力 pod亲和力 pod反亲和力 节点亲和力 NodeAffinity 进行 pod 调度时,优先调度到符合条件的亲和力节点上 可配置 硬亲和力和软亲和力 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.路由器的两种工作模式 路由 作用:对路由进行权限控制 分类:全局守卫,独享守卫,组件内守卫 一.全局前置路由和后置路由 ① 前置路由守卫:每次路由…...
第三章 图论 No.1单源最短路及其综合应用
文章目录 1129. 热浪1128. 信使1127. 香甜的黄油1126. 最小花费920. 最优乘车903. 昂贵的聘礼1135. 新年好340. 通信线路342. 道路与航线341. 最优贸易 做乘法的最短路时,若权值>0,只能用spfa来做,相等于加法中的负权边 1129. 热浪 1129.…...
❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件
❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件 cmd或者终端用nvm 安装提示: npm不是内部或外部命令,也不是可运行的程序或批处理文件 原因(一) 提示这个问题,有可能是Node没有安装,也有可能是没有配置…...
关于Godot游戏引擎制作流水灯
先上核心代码 游戏节点 流水灯的通途可以是 1. 装饰 2. 音乐类多媒体程序(如FL中TB-303的步进灯) FL Studio Transistor Bass...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...






















