C语言——动态内存管理
目录
- 0. 思维导图:
- 1. 为什么存在动态内存分配
- 2. 动态内存函数介绍
- 2.1 malloc和free
- 2.2 calloc
- 2.3 realloc
- 3. 常见的动态内存错误
- 3.1 对NULL指针的解引用操作
- 3.2 对动态内存开辟的空间越界访问
- 3.3 对非动态开辟内存使用free释放
- 3.4 使用free释放一块动态开辟内存的一部分
- 3.5 对同一块动态内存多次释放
- 3.6 动态内存开辟忘记释放(内存泄漏)
- 4. C/C++程序的内存开辟
0. 思维导图:

1. 为什么存在动态内存分配
int a = 10;//在栈空间开辟四个字节int arr[10] = { 0 };//在栈空间上开辟40个字节的连续空间
此类开辟空间的方式有两个特点:
- 空间开辟大小是固定的;
- 数组在申明的时候,必须指定数组长度,它所需要的内存在编译时分配(变长数组是不能改变数组的大小的,仅仅是允许数组的大小可用变量指定)。
但是对于空间大小的要求,有时候是需要在程序运行的时候才知道的,这时就需要使用动态内存开辟了。
2. 动态内存函数介绍
C语言分为3种内存池:栈区、堆区、静态区,而我们的动态内存函数,是属于堆区。

2.1 malloc和free
malloc参数及返回类型:
void* malloc (size_t size);
malloc可以向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针(所以在使用malloc时,一定要先检查返回值,看是否开辟成功)。
- 返回值的类型是 *void,具体使用什么类型,由使用者来决定。
- 如果size为0,malloc的行为是标准未定义的,取决于编译器。
(该行为毫无意义,就好比找人借钱
A:兄弟,最近手头有点紧,借点钱花花。
B:借多少?
A:借0元
B:滚!)
因为malloc是在堆区上申请的内存空间,使用完毕之后需要将内存归还,所以C语言提供了内外一个free函数,专门用来做动态内存的释放和回收的。
free的参数及返回类型:
void free (void* ptr);
free函数用来释放动态开辟的内存。
- 如果参数ptr指向的空间不是动态内存开辟的,那free函数的行为是未定义的。
- 如果参数ptr是NULL指针,则函数什么事都不做。
malloc和free的声明都在stdlib.h头文件中,在使用时需引用头文件。
代码示例:
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{//申请40个字节,用来存放10个整型int* ptr = (int*)malloc(40);if (ptr == NULL)//判断ptr是否申请成功{printf("%s\n", strerror(errno));return 1;}int i = 0;for (i = 0; i < 10; i++){*(ptr + i) = i + 1;printf("%d ", *(ptr + i));}//释放内存free(ptr);//如果不将ptr设置为空,则ptr将是野指针,所以需要我们主动置空ptr = NULL;return 0;
}
2.2 calloc
C语言还提供了一个函数叫calloc ,calloc函数也用来动态内存分配。
calloc的参数及返回类型:
void* calloc (size_t num, size_t size);
- 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
- 与malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0。
代码示例:
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{int* ptr = (int*)calloc(10, sizeof(int));if (ptr == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i < 10; i++){*(ptr + i) = i + 1;printf("%d ", *(ptr + i));}free(ptr);ptr = NULL;return 0;
}
malloc申请到的空间,没有初始化,直接返回起始地址;
calloc申请到空间之后,会把空间初始为0,再返回起始地址。
如果申请的内存要求初始化,那么可用很方便使用calloc函数。
不过,因为malloc不需要初始化,所以整体来说malloc的效率会稍高于calloc。
2.3 realloc
realloc函数的出现让动态内存的管理更加灵活,有时申请的空间大了,有事申请的空间又小了,那么为了合理的内存分配,就会使用realloc对动态内存就行调整。
realloc的参数及返回类型:
void* realloc (void* ptr, size_t size);
- ptr是需调整的内存地址;
- size调整之后的新大小;
- 返回值为调整之后的起始地址;
- 这个函数调整原内存空间大小的基础上,还好将原来内存中的数据移动到新的空间。
- realloc调整内存空间存在的两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间

代码示例:
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{int* p = (int*)malloc(5 * sizeof(int));if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 5; i++){*(p + i) = 1;}//再向内存申请5个整型的空间//此时用新的指针地址接收,防止realloc申请失败,把原有的地址覆盖int* ptr = (int*)realloc(p, 10 * sizeof(int));if (ptr != NULL){p = ptr;}for (i = 5; i < 10; i++){*(p + i) = 1;}free(ptr);ptr = NULL;return 0;
}
3. 常见的动态内存错误
3.1 对NULL指针的解引用操作
int main()
{int*p = (int*)malloc(INT_MAX);//未对malloc的返回值进行判断int i = 0;for (i = 0; i < 10; i++){*(p + i) = 0;}free(p);p = NULL;return 0;
}
3.2 对动态内存开辟的空间越界访问
int main()
{int* p = (int*)malloc(100);//向内存申请了100个字节空间if (p = NULL){return 1;}int i = 0;for (i = 0; i < 100; i++){//此时访问的是100个整型的空间,应该需要400个字节//越界访问*(p + i) = 0;}free(p);p = NULL;return 0;
}
3.3 对非动态开辟内存使用free释放
int main()
{int a = 0;//栈区int* p = &a;free(p);p = NULL;
}
3.4 使用free释放一块动态开辟内存的一部分
int main()
{int* p = (int*)malloc(100);if (p == NULL){return 1;}int i = 0;for (i = 0; i < 25; i++){*p = i;//p的地址发生改变p++;}free(p);//p未指向起始地址p = NULL;return 0;
}
3.5 对同一块动态内存多次释放
int main()
{int* p = (int*)malloc(100);if (p == NULL){return 1;}free(p);//...//将p释放,但未置空,此时p为野指针//如果将p释放后置空,那么在释放一次,free的参数为null,函数什么都不做free(p);return 0;
}
3.6 动态内存开辟忘记释放(内存泄漏)
void test()
{int* p = (int*)malloc(100);if (NULL != p){*p = 20;}
}
int main()
{test();while (1);
}
忘记释放不再使用的动态开辟的空间会造成内存泄漏,程序会一直吃内存(如下图)

使用malloc和free一定要成对使用。
4. C/C++程序的内存开辟
C/C++程序内存区域划分:

C/C++程序内存分配的几个区域:
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
- 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
- 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
相关文章:
C语言——动态内存管理
目录0. 思维导图:1. 为什么存在动态内存分配2. 动态内存函数介绍2.1 malloc和free2.2 calloc2.3 realloc3. 常见的动态内存错误3.1 对NULL指针的解引用操作3.2 对动态内存开辟的空间越界访问3.3 对非动态开辟内存使用free释放3.4 使用free释放一块动态开辟内存的一部…...
Docker安装Grafana
文章目录Grafana介绍拉取镜像准备相关挂载目录及文件启动容器访问测试添加 Prometheus 数据源常见问题看板配置Grafana介绍 上篇博客介绍了prometheus的安装: Docker部署Prometheus 在获取应用或基础设施运行状态、资源使用情况,以及服务运行状态等直观…...
数据结构(四):树、二叉树、二叉搜索树
数据结构(四)一、树1.树结构2.树的常用术语二、二叉树1.什么是二叉树2.二叉树的数据存储(1)使用数组存储(2)使用链表存储三、二叉搜索树1.这是什么东西2.封装二叉搜索树:结构搭建3. insert插入节…...
040、动态规划基本技巧(labuladong)
动态规划基本技巧 一、动态规划解题套路框架 基于labuladong的算法网站,动态规划解题套路框架; 1、基本介绍 基本套路框架: 动态规划问题的一般形式是求最值;核心如下: 穷举;明确base case;…...
html笔记(一)
一、html简介 什么是HTML? Hyper Text Markup Language 超文本标记语言 超文本?超级文本,例如流媒体,声音、视频、图片等。 标记语言?这种语言是由大量的标签组成。 任何一个标签都有开始标签和结束标签&…...
索引的情况
select * from A left join B on A.c B.c where A.employee_id 3 1.一句sql中 是可能走多次索引的,具体的 一般 表连接 ,或者说生成临时表的时候,会走索引 然后条件过滤的时候也会走索引,具体的 还是要具体分析 2.表连接 字段…...
Verilog 学习第五节(串口发送部分)
小梅哥串口部分学习part1 串口通信发送原理串口通信发送的Verilog设计与调试串口发送应用之发送数据串口发送应用之采用状态机实现多字节数据发送串口通信发送原理 1:串口通信模块设计的目的是用来发送数据的,因此需要有一个数据输入端口 2:…...
破解遗留系统快速重构的5步心法(附实例)
前两天和一个架构师朋友闲聊,说到了 「重构」 这个话题,他们公司早年间上线的项目系统,因一直没专人在演进过程中为代码质量负责,导致现在代码越来越混乱,逐渐堆积成“屎山”,目前的维护成本已远高于重新开…...
信号量(上)实验
实验1:解决订票终端的临界区管理 订票终端是解决冲突问题,所以信号量的值是1 #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> int ticketAmout 2; // 票的数量: 全局变量 sem_t mutex…...
阿里5年,一个女工对软件测试的理解
成为一个优秀的测试工程师需要具备哪些知识和经验? 针对这个问题,可以直接拆分以下三个小问题来详细说明: 1、优秀软件测试工程师的标准是什么? 2、一个合格的测试工程师需要具备哪些专业知识? 3、一个合格的测试工程…...
前端练习项目
30 Web Projects 30 多个带有 HTML、CSS 和 JavaScript 的 Web 项目,由 Packt Publishing 提供 https://github.com/PacktPublishing/30-Web-Projects-with-HTML-CSS-and-JavaScript Small projects https://github.com/WebDevVikramChoudhary/small_projects_for_…...
sql复习(set运算符、高级子查询)
一、set运算符 union:得到两个查询结果的并集,并且⾃动去掉重复⾏。不会排序 union all:得到两个查询结果的并集,不会去掉重复⾏。也不会排序 intersect:得到两个查询结果的交集,并且按照结果集的第⼀个列进…...
整车电源的几种模式:OFF/ACC/RUN/CRANK
本文框架1.前言2. 四种电源模式2.1 OFF模式2.2 ACC模式2.3 ON模式2.4 CRANK模式3. KL15/KL301.前言 在诊断或者网络管理相关模块开发对客户的需求进行梳理时,经常会看到客户对不同车辆模式下处理策略的需求,如果前期没接触过这几种模式,可能…...
踩了大坑:wordpress后台 无法将上传的文件移动至wp-content
一、问题描述 今天迁移了wordpress站点至新服务器,结果上传图片出现“无法将上传的文件移动至wp-content/uploads”的提示,这是怎么回事,为什么会这样。 报错如下: 2023/02/20 08:57:48 [error] 9861#9861: *79624 FastCGI sen…...
page cache设计及实现
你好,我是安然无虞。 page cache的设计及实现 page cache 本质上也是一个哈希桶, 它是按照页的数量进行映射的. 当 central cache 向 page cache 申请内存时, page cache 先检查对应位置是否有span, 如果没有则向更大页去寻找一个span, 如果找到则分裂成两个. 比如…...
使用seata来解决分布式事务
文章目录 目录 文章目录 前言 一、Seata的执行流程如下 二、使用步骤 三、配置微服务客户端 总结 前言 Seata部署指南 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模…...
推荐一款新的自动化测试框架:DrissionPage
今天给大家推荐一款基于Python的网页自动化工具:DrissionPage。这款工具既能控制浏览器,也能收发数据包,甚至能把两者合而为一,简单来说:集合了WEB浏览器自动化的便利性和 requests 的高效率。 一、DrissionPage产生背…...
MQ系列面试
先来说说什么是MQ,MQ与多线程之间的区别MQ是消息中间件 可以实现异步 多线程也可以实现异步使用传统http协议方式调用接口存在的缺点如果服务器端没有及时的响应给客户端的时候,容易造成客户端阻塞等待。服务器响应超时 客户端发送重试机制 需要考虑避免…...
一句话设计模式2:原型模式
原型模式:每次得到一个新对象。 文章目录 原型模式:每次得到一个新对象。前言一、原型模式和new的区别二、如何实现原型模式1. 什么clone接口2. 开始使用,并验证浅clone效果3. 深度clone(也就是address也要复制一份)总结前言 原型模式可以说是目前接触的设计模式中,比较无用的…...
c++11特性与c++17特性
1、自动类型推导auto // C11 auto func1() -> int // 需要指定返回值类型 {return 10; }auto func2() -> std::function<void()> {auto lambda []() { };return lambda; }// c17 // 之后无需指定返回值类型 auto func1() {return 10; }auto func2() {auto lambda…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配) ❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞ 你给的定义拆解: ✴ Instead of …...
Spring是如何实现无代理对象的循环依赖
无代理对象的循环依赖 什么是循环依赖解决方案实现方式测试验证 引入代理对象的影响创建代理对象问题分析 源码见:mini-spring 什么是循环依赖 循环依赖是指在对象创建过程中,两个或多个对象相互依赖,导致创建过程陷入死循环。以下通过一个简…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...

