【C语言:动态内存管理】
文章目录
- 前言
- 1.malloc
- 2.free
- 3.calloc
- 4.realloc
- 5.动态内存常见错误
- 6.动态内存经典笔试题分析
- 7.柔性数组
- 8.C/C++中的内存区域划分

前言
文章的标题是动态内存管理,那什么是动态内存管理?为什么有动态内存管理呢?
回顾一下以前学的知识,我们已经掌握的开辟内存的方式有以下几种:
char ch = 'a';
int val = 10;
float f_val = 12.8f;
int arr[20] = { 0 };
//....
上述开辟内存的方式有几个弊端:
- 开辟空间的大小是固定的
- 数组在声明的时候,必须指定数组的长度,数组空间⼀旦确定了大小就不能调整了
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组在编译时就开辟空间的方式就不能满⾜了。
C语言引⼊了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。
1.malloc

这个函数的功能是:向内存申请一块连续的内存空间,然后返回一个指向该空间的起始地址的指针。
- 如果空间
开辟成功,则返回一个指向该空间起始地址的指针。 - 如果空间
开辟失败,则返回一个NULL指针。因此,函数的返回值需要做检查 - 开辟的空间不会被初始化。
- 返回值的类型是void*,所以该函数并不知道所开辟空间的类型,需要空间的使用者自己决定。
- 当该函数的参数是0是,返回值是未定义的,取决于编译器。
- 头文件stdlib.h
使用malloc函数申请40个字节的空间

当函数的参数是0时,vs2019中是随机值

既然空间是程序员申请的,那么最后由谁释放呢?
- 程序员自己释放。
- 程序结束,操作系统回收
下面就来介绍如何释放
2.free
C语言提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的,函数原型如下:

- 该函数用来释放由malloc、calloc、realloc申请的空间的
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数 ptr 是NULL指针,则函数什么事都不做。
- 该函数不会改变指针ptr的值,因此为了避免野指针的出现,要将其及时置为NULL。

3.calloc

该函数的功能跟malloc差不多,区别在于:
- 该函数的参数有两个,一个是有几个元素,一个是每个元素的大小。
- 该函数会将申请的空间初始化为全0。

4.realloc

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使用内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
- ptr 是要调整的内存地址
- size 调整之后新大小
- 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间大小的基础上,还会
将原来内存中的数据移动到新的空间。 - realloc在调整内存空间的是存在两种情况:
- 情况1:原有空间之后有足够大的空间
- 情况2:原有空间之后没有足够大的空间
- 情况1
当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。- 情况2
当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩连续空间来使⽤。这样函数返回的是⼀个新的内存地址。
既然该函数有两种特殊情况,那我们在使用时也应该更加注意:
下面的代码有没有问题呢?
int main()
{//1. 先申请一部分空间int* p = (int*)malloc(20);if (NULL == p){perror("malloc");return 1;}//2.扩容空间p = (int*)realloc(p, 40);if (NULL == p){perror("realloc");return 1;}//3.使用//.......free(p);p = NULL;return 0;
}
假如realloc开辟空间失败了,那么它会返回一个空指针,p就是空指针了。
好家伙,没给我扩容成功,我以前的数据也没了,真可以!
所以在接收realloc的返回值时,我们应该使用你一个临时变量接收,判断临时变量是不是空指针,然后再对p进行操作。
所以,应该这样写:
#include<stdlib.h>int main()
{//1. 先申请一部分空间int* p = (int*)malloc(20);if (NULL == p){perror("malloc");return 1;}//2.扩容空间int* tmp = NULL;tmp = (int*)realloc(p, 40);//先判断realloc的返回值if (tmp != NULL){p = tmp;tmp = NULL;}//3.使用//....free(p);p = NULL;return 0;
}
5.动态内存常见错误
- 对NULL指针的解引用操作
- 对动态开辟空间的越界访问
- 对非动态开辟内存使用free释放

- 使用free释放⼀块动态开辟内存的⼀部分

- 对同⼀块动态内存多次释放

- 动态开辟内存忘记释放(内存泄漏)
6.动态内存经典笔试题分析
- 题目一
void GetMemory(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}
此处是值传递,并不会为str真正开辟空间,str还是NULL。

- 题目二
char* GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}
p变量生命周期为GetMemory函数内,函数结束,变量销毁。使用一个已经被销毁的变量的地址,结果是不可预测的

3.题目三
void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf(str);}
}
str指向的空间被释放,那么str就是野指针了,但是str还是指向那块内存空间;向str中拷贝world,那就是非法访问了。
7.柔性数组
- 什么是柔性数组
也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99 中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
struct s
{int i;char ch;int a[]; //int a[0]
};
其中a就是一个柔性数组。
- 柔性数组的特点
- 结构中的柔性数组成员前⾯ 必须⾄少⼀个其他成员。
- sizeof 返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构体用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。
- 柔性数组的使用

如果我们不使用柔性数组,那么如何实现上述代码呢?

虽然不适用柔性数组也能达到目的,但使用柔性数组有两个好处:
- 方便内存的释放
如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。
- 有利于访问速度
连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。
8.C/C++中的内存区域划分

- 栈区
栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
- 堆区
堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅方式似于链表。
- 静态区
数据段(静态区):存放全局变量、static修饰的变量。程序结束后由系统释放。
- 代码段
代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。
本次的分享就到这里啦!如有错误请纠正。
相关文章:
【C语言:动态内存管理】
文章目录 前言1.malloc2.free3.calloc4.realloc5.动态内存常见错误6.动态内存经典笔试题分析7.柔性数组8.C/C中的内存区域划分 前言 文章的标题是动态内存管理,那什么是动态内存管理?为什么有动态内存管理呢? 回顾一下以前学的知识ÿ…...
【Python基础】迭代器
文章目录 [toc]什么是迭代可迭代对象判断数据类型是否是可迭代类型 迭代器对可迭代对象进行迭代的本质获取可迭代对象的迭代器通过迭代器获取数据StopIteration异常 自定义迭代器__iter__()方法__next__()方法判断数据类型是否是可迭代类型自定义迭代器案例分离模式整合模式 fo…...
QVTK 可视化
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>#include <vtkNew.h> // 智能指针 #include <QVTKOpenGLNativeWidget.h> #include <vtkCylinderSource.h> // 圆柱#include <vtkPolyDataMapper.h&g…...
【初阶C++】入门(超详解)
C入门 前言1. C关键字(C98)2. 命名空间2.1 命名空间定义2.2 命名空间使用2.3嵌套命名空间 3. C输入&输出4. 缺省参数4.1 缺省参数概念4.2 缺省参数分类 5. 函数重载5.1 函数重载概念5.2 C支持函数重载的原理--名字修饰(name Mangling) 6. 引用6.1 引用概念6.2 引用特性6.3 …...
Java正则表达式的使用
标题:Java正则表达式的使用 介绍: 正则表达式是一种强大的文本匹配模式和搜索工具。在Java中,通过使用正则表达式,我们可以快速有效地进行字符串的匹配、查找和替换。本文将介绍Java正则表达式的基本使用方法,并提供相…...
Collecting Application Engine Performance Data 收集应用程序引擎性能数据
You can collect performance data of any specific SQL action of an Application Engine program to address any performance issue. 您可以收集应用程序引擎程序的任何特定SQL操作的性能数据,以解决任何性能问题。 You can collect performance data of the S…...
C Primer Plus阅读--章节16
C Primer Plus阅读–章节16 翻译程序的第一步 预处理之前,编译器必须对该程序进行一些翻译处理。 首先,编译器将源代码中出现的字符映射到源字符集。第二,编译器定位每个反斜杠后面跟着换行符的实力,并删除他们。物理行的合并。…...
直接插入排序与希尔排序
目录 前言 插入排序 直接插入排序 时空复杂度 直接插入排序的特性 希尔排序(缩小增量排序) 预排序 顺序排序 多组并排 小总结 直接插入排序 时空复杂度 希尔排序的特性 前言 字可能有点多,但是真的理解起来真的没那么难&#…...
敏捷:应对软件定义汽车时代的开发模式变革
随着软件定义汽车典型应用场景的落地,汽车从交通工具转向智能移动终端的趋势愈发明显。几十年前,一台好车的定义主要取决于高性能的底盘操稳与动力系统;几年前,一台好车的定义主要取决于智能化系统与智能交互能否满足终端用户的用…...
做题笔记:SQL Sever 方式做牛客SQL的题目--查询每天刷题通过数最多的前二名用户
----查询每天刷题通过数最多的前二名用户id和刷题数 现有牛客刷题表questions_pass_record,请查询每天刷题通过数最多的前二名用户id和刷题数,输出按照日期升序排序,查询返回结果名称和顺序为: date|user_id|pass_count 表单创建…...
Vue3 用 Proxy API 替代 defineProperty API 的那些事
一、Object.defineProperty 定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 1.1 为什么能实现响应式 通过defineProperty 两个属性,get及set get 属性的 gett…...
成都工业学院Web技术基础(WEB)实验五:CSS3动画制作
写在前面 1、基于2022级计算机大类实验指导书 2、代码仅提供参考,前端变化比较大,按照要求,只能做到像,不能做到一模一样 3、图片和文字仅为示例,需要自行替换 4、如果代码不满足你的要求,请寻求其他的…...
【Docker】学习笔记(三)三剑客之 docker-compose文件书写项目多服务容器运行
简介 引言(需求) 为了完成一个完整项目势必用到N多个容器配合完成项目中的业务开发,一旦引入N多个容器,N个容器之间就会形成某种依赖,也就意味着某个容器的运行需要其他容器优先启动之后才能正常运行; 容…...
node.js基础
node.js基础 🍓什么是node.js🍓node.js模块🍒🍒 内置模块🍅🍅🍅fs模块🍅🍅🍅path模块🍅🍅🍅http模块 🍒&#…...
fastapi实现websocket在线聊天
最近要实现一个在线聊天功能,基于fastapi的websocket实现了这个功能。下面介绍一下遇到的技术问题 1.问题难点 在线上环境部署时,一般是多进程的方式进行部署启动fastapi服务,而每个启动的进程都有自己的独立存储空间。导致存储的连接对象分…...
LLM推理部署(六):TogetherAI推出世界上LLM最快推理引擎,性能超过vLLM和TGI三倍
LLM能有多快?答案在于LLM推理的最新突破。 TogetherAI声称,他们在CUDA上构建了世界上最快的LLM推理引擎,该引擎运行在NVIDIA Tensor Core GPU上。Together推理引擎可以支持100多个开源大模型,比如Llama-2,并在Llama-2–…...
Unity | 渡鸦避难所-2 | 搭建场景并添加碰撞器
1 规范项目结构 上期中在导入一系列的商店资源包后,Assets 目录已经变的混乱不堪 开发过程中,随着资源不断更新,遵循一定的项目结构和设计规范是非常必要的。这可以增加项目的可读性、维护性、扩展性以及提高团队协作效率 这里先做下简单的…...
展望2024年供应链安全
2023年是开展供应链安全,尤其是开源治理如火如荼的一年,开源治理是供应链安全最重要的一个方面,所以我们从开源治理谈起。我们先回顾一下2023的开源治理情况。我们从信通院《2023年中国企业开源治理全景观察》发布的信息。信通院调研了来自七…...
React 列表页实现
一、介绍 列表页是常用的功能,从后端获取列表数据,刷新到页面上。开发列表页需要考虑以下技术要点:1.如何翻页;2.如何进行内容搜索;3.何时进行页面刷新。 二、使用教程 1.user-service 根据用户id获取用户列表,返回…...
【程序人生】还记得当初自己为什么选择计算机?
✏️ 初识计算机: 还记得人生中第一次接触计算机编程是在高中,第一门编程语言是Python(很可惜由于条件限制的原因,当时没能坚持学下去......现在想来有点后悔,没能坚持,唉......)。但是…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
docker容器互联
1.docker可以通过网路访问 2.docker允许映射容器内应用的服务端口到本地宿主主机 3.互联机制实现多个容器间通过容器名来快速访问 一 、端口映射实现容器访问 1.从外部访问容器应用 我们先把之前的删掉吧(如果不删的话,容器就提不起来,因…...
