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

C语言——内存管理

目录

前言

一、内存分类

1. 栈区(Stack)

2. 堆区(Heap)

3. 数据段(Data Segment)

4. 代码段(Code Segment)

二、内存分配方式

1、静态内存分配

2、栈内分配

3、动态内存分配

(1)如何使用malloc 函数:

 (2)内存释放

 (3)内存泄漏

三、常见内存错误——野指针

1、未初始化的指针

2、 指针释放后未置空

3、局部变量指针逃逸

4、指针运算错误

完结



前言

        C语言中,内存管理需要对静态和动态内存分配,静态分配在编译时确定,而动态分配(如malloc, calloc, realloc)则在运行时进行,需程手动管理,包括适时释放(用free)以避免内存泄漏。同时,了解栈、堆、数据区、代码区等内存区域的特性和用途,对于有效管理内存至关重要。


一、内存分类

        可将内存简单分为:栈区、堆区、静态区,其中静态区包含数据段、代码段的内容,主要存储常量、字符串常量等只读数据、已初始化的全局变量和静态变量以及未初始化的全局变量和静态变量。

        堆栈主要指的是栈,而不是堆。

1. 栈区(Stack)

定义:栈区用于存储函数的局部变量、函数参数和返回地址。栈区内存由系统自动分配和释放,具有后进先出(LIFO)的特性。

特点:1、分配效率高,但空间有限;

           2、编译器自动管理,无需程序员手动释放。

用于保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容 也会自动被销毁。

2. 堆区(Heap)

定义:堆区用于动态分配内存,即程序运行时使用malloc、new等函数分配的内存。

特点:1、可以动态分配和释放大块内存。

           2、内存由程序员手动管理,需要手动释放(使用free函数)。

           3、内存分配效率相对较低,但空间较大。

其生命周期由 free 或 delete 决定。 在没有释放之前一直存在,直到程序结束。

3. 数据段(Data Segment)

数据段又可以进一步细分为:

1、已初始化数据段:存储已初始化的全局变量和静态变量(static)。这些变量在程序加载时分配内存,并在程序结束时释放。

2、未初始化数据段(BSS段):存储未初始化的全局变量和静态变量。BSS段在程序启动时自动初始化为零或空指针,但它在程序加载时并不占用磁盘空间(因为其内容默认为零)。

4. 代码段(Code Segment)

定义:代码段也称为文本段,存储程序的机器指令。

特点:1、通常是只读的,以防止程序意外修改指令。

           2、包含常量字符串等只读数据。

函数定义和字符串常量存储在代码段。

二、内存分配方式

1、静态内存分配

        静态内存分配是在程序编译时进行的,它将内存分配给全局变量和静态变量,存在周期最长。

优点:内存分配和释放的效率高。

缺点:内存使用不灵活,无法根据需要动态调整内存大小。

char UART_RxBUF[100] = {0};//已初始化全局变量
char flag;//未初始化全局变量static int count;//静态全局变量,只能在本文件内使用void fun()
{static int i;//静态变量//fun主体
}

2、栈内分配

        栈内存分配是在程序运行时进行的,它将内存分配给函数内部的局部变量。

优点:内存管理简单,不需要程序员手动释放。

缺点:内存空间有限,不适合分配大内存,且存在栈溢出的风险。

void fun()
{int i = 0;char buf[100] = {0};//函数运行结束,就会释放
//fun主体
}

3、动态内存分配

        动态内存分配是在程序运行时根据需要进行的内存分配。

优点:内存使用灵活,可以根据需要动态调整内存大小。

缺点:内存管理复杂,需要程序员手动分配和释放,容易出现内存泄漏等问题。

        常用的动态内存分配函数包括malloc()、calloc()realloc(),分别用于分配内存、分配并初始化为0的内存、以及重新调整已分配内存的大小。

(1)如何使用malloc 函数:

        其原型为:

(void *)malloc(int size)

 malloc 函数的返回值是一个 void 类型的指针,参数为 int 类型数据,即申请分配的内存 大小,单位是 byte。内存分配成功之后,malloc 函数返回这块内存的首地址。你需要一个指 针来接收这个地址。但是由于函数的返回值是 void *类型的,所以必须强制转换成你所接收 的类型。也就是说,这块内存将要用来存储什么类型的数据。比如:

int *ptr = (int*)malloc(10 * sizeof(int));
//分配了10个整数(int)的内存空间

 在堆上分配了 10 个int类型的内存(若在编译器中int占四个字节,那么分配内大小为40个字节),返回这块内存的首地址,把地址强制转换成 int *类型后赋给 int *类型的指针变量 ptr。同时告诉我们这块内存将用来存储 int类型的数据。也就是说只能通过指针变量 ptr 来操作这块内存。这块内存本身并没有名字,对它的访问是匿名访问。

        上面就是使用 malloc 函数成功分配一块内存的过程。但是,每次你都能分配成功吗? 不一定使用 malloc 函数同样要注意:如果所申请的内存块大于目前堆上剩余内存块,则内存分配会失败,函数返回 NULL。

注意:这里说的“堆上剩余内存块”不是所有剩余内存块之和,因 为 malloc 函数申请的是连续的一块内存,所以只要堆上剩余的、连续的内存块没有能满足所申请的内存大小就会申请失败。

既然 malloc 函数申请内存有不成功的可能,那我们在使用指向这块内存的指针时,必须用 if(NULL != p)语句来验证内存确实分配成功了。

int *ptr = (int*)malloc(10 * sizeof(int));
//分配了10个整数(int)的内存空间if(ptr != NULL)
{printf("内存申请成功!\n");
}

        如果用 malloc 函数申请 0 字节内存时,函数并 不返回 NULL,而是返回一个正常的内存地址。但是你却无法使用这块大小为 0 的内存。这时候 if(NULL != p)语句校验将不起作用。

 (2)内存释放

        既然有分配,那就必须有释放。在C语言中,使用动态内存分配后,程序员需要负责在适当的时候释放这些内存,不然的话,有限的内存总会用光,而没有释放的内存却在空闲,导致内存泄漏。与 malloc 对应的就是 free 函数了。free 函数只有一个参数,就是所要释放的内存块的首地址,该函数只能用于释放通过malloc()、calloc()或realloc()等函数分配的内存。比如上例:

int *ptr = (int*)malloc(10 * sizeof(int));
if(ptr != NULL)
{printf("内存申请成功!\n");
}free(ptr);ptr = NULL;

         切记,申请次数和释放次数要对应。malloc 两次只 free 一次会内存泄漏;malloc 一次 free 两次肯定会出错。也就是说,在程序 中 malloc 的使用次数一定要和 free 相等,否则必有错误。虽然使用 free 函数释放了内存,但指针变量 p 本身保存的地址并没有改变,就需要重新把 p 的值变为 NULL:否则这个指针就成为了“野指针”,也有书叫“悬 垂指针”。这是很危险的,而且也是经常出错的地方。所以一定要记住一条:free 完之后, 一定要给指针置 NULL。

 (3)内存泄漏

        内存泄漏(Memory Leak)是指程序中已分配的内存由于某种原因未被释放或无法释放,导致该内存块持续被占用,无法再被程序或系统用于其他目的。随着内存泄漏的不断积累,可用内存逐渐减少,最终可能导致程序运行速度变慢、响应迟缓,甚至崩溃。在程序中动态分配的内存(如使用 malloc、new 等函数或操作符)在不再需要时,应当通过相应的释放函数(如 free、delete)进行释放。如果忘记释放,就会导致内存泄漏。

三、常见内存错误——野指针

        野指针是一个指针变量,所指向的地址是未知的、随机的、不正确的或没有明确限制的,以及那些已经被释放的内存地址。这种指针在尝试访问或修改其所指向的内存时,会导致不可预测的行为,甚至程序崩溃。野指针的存在是编程中的一个严重问题,因为它可能导致程序崩溃、数据损坏或其他不可预期的行为。在严重的情况下,野指针还可能被恶意利用,造成安全漏洞。

1、未初始化的指针

        在C或C++中,声明一个指针变量时,如果没有立即为其分配内存或初始化为NULL,它将包含一个随机的地址。尝试访问这个随机地址会导致不可预知的后果。如:

#include <stdio.h>struct student
{
char *name;
int score;
}stu;int main()
{
strcpy(stu.name,"Jimy");
return 0;
}
/*这里定义了结构体变量 stu,但是他没想到这个结构体内部 char *name 
这成员在定义结构体变量 stu 时,只是给 name 这个指针变量本身分配了 
4 个字节。name 指针并没有指向一个合法的地址,这时候其内部存的只是
一些乱码。所以在调用 strcpy 函数时,会将字符串"Jimy"往乱码所指的内
存上拷贝,而这块内存 name 指针根本就无权访问,导致出错。解决的办法
是为 name 指针 malloc 一块空间。*/

2、 指针释放后未置空

        使用free()delete等函数释放了指针指向的内存后,如果不将指针置为NULL,那么这个指针就变成了野指针。在后续的代码中,如果错误地尝试通过这个指针访问内存,将会导致不可预测的行为。

int *ptr = (int*)malloc(10 * sizeof(int));
if(ptr != NULL) printf("内存申请成功!\n");
free(ptr);// ptr = NULL;
*ptr = 100;
*(ptr+1) = 200;
//ptr已经被释放,错误操作

3、局部变量指针逃逸

        当函数返回时,其栈上的局部变量将不再有效。如果指针仍然指向这些局部变量,它们将成为野指针。如:

#include <stdio.h>  int* fun() 
{  int value = 10; // 局部变量  return &value; // 返回局部变量的地址  
}  int main(){  int* ptr = fun(); // ptr 指向了 fun函数中的局部变量 value 的地址  printf("%d\n", *ptr); // 这里可能打印出 10,但这是未定义行为  // 当 fun函数执行完毕后,value 所占用的内存已经被释放  // 此时 ptr 是一个野指针,访问它会导致未定义行为  // 但在某些情况下(比如没有立即重用该内存区域),它可能看起来还在工作  return 0;  
}

 正确方式:

#include <stdio.h>  
#include <stdlib.h>  
// 函数声明,返回一个指向int的指针  
int* createInteger(int value) {  // 在堆上分配内存来存储一个int值  int* ptr = (int*)malloc(sizeof(int));  if (ptr == NULL) {  // 如果malloc失败,返回NULL  return NULL;  }  *ptr = value; // 初始化内存中的值  return ptr; // 返回指向新分配内存的指针  
}  // 注意:在C语言中,我们通常不编写专门的函数来释放内存,因为这通常是调用者的责任  int main() {  int* myInteger = createInteger(42); // 调用函数,分配内存并初始化  if (myInteger != NULL) {  // 检查指针是否为NULL,以避免解引用空指针  printf("The value is: %d\n", *myInteger); // 使用指针访问值  // ... 在这里可以使用myInteger做一些事情 ...  // 释放之前分配的内存  free(myInteger);  // 将指针置为NULL,避免成为野指针(这是一个好习惯)  myInteger = NULL;  } else {  // 处理内存分配失败的情况  printf("Memory allocation failed!\n");  }  return 0;  
}

4、指针运算错误

        对指针进行算术运算时,如果运算后的指针指向了未知或无效的内存区域,也会形成野指针。如:

#include <stdio.h>  int main() {  int arr[5] = {1, 2, 3, 4, 5}; // 定义一个有5个元素的数组  int *ptr = arr; // 指针ptr指向数组的第一个元素  // 正确访问数组元素  for (int i = 0; i < 5; i++) {  printf("%d ", *(ptr + i)); // 输出1 2 3 4 5  }  // 指针运算错误:尝试访问数组之外的内存  ptr += 5; // ptr现在指向arr[5],但实际上arr[5]不存在,这是越界  printf("%d\n", *ptr); // 尝试解引用ptr,这是未定义行为  // 在某些情况下,上面的代码可能不会立即崩溃,但会导致不可预测的结果  // 因为ptr现在指向了一个未定义的内存位置  return 0;  
}

完结

有误之处望指正!!!

相关文章:

C语言——内存管理

目录 前言 一、内存分类 1. 栈区&#xff08;Stack&#xff09; 2. 堆区&#xff08;Heap&#xff09; 3. 数据段&#xff08;Data Segment&#xff09; 4. 代码段&#xff08;Code Segment&#xff09; 二、内存分配方式 1、静态内存分配 2、栈内分配 3、动态内存分配 &#x…...

Unity UGUI 之 Image和Rawimage

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 1.Image是什么 Unity - 手册&#xff1a;图像 精灵格式是什么&#xff1f; 1.2重要参数 …...

Lua 语法学习笔记

Lua 语法学习笔记 安装(windows) 官网&#xff1a;https://www.lua.org/ 下载SDK 解压&修改名称&#xff08;去除版本号&#xff09; 将lua后面的版本号去掉&#xff0c;如lua54.exe->lua.ext 配置环境变量 数据类型 数据类型描述nil这个最简单&#xff0c;只有值n…...

Prometheus配置alertmanager告警

1、拉取镜像并运行 1、配置docker镜像源 [rootlocalhost ~]# vim /etc/docker/daemon.json {"registry-mirrors": ["https://dfaad.mirror.aliyuncs.com"] } [rootlocalhost ~]# systemctl daemon-reload [rootlocalhost ~]# systemctl restart docker2、…...

.net core 外观者设计模式 实现,多种支付选择

1&#xff0c;接口 /// <summary>/// Web页面支付/// </summary>public interface IWebPagePay{public WebPagePayResult CreatePay(string productName, string orderSn, string totalPrice);}2&#xff0c;实现接口 实现阿里支付 public class AliPagePay : IWe…...

Matlab 命令行窗口默认输出(异常)

目录 前言Matlab 先验知识1 异常输出的代码2 正常输出的代码 前言 在单独调试 Matlab 写的函数时出现不想出现的异常打印值&#xff0c;逐个注释排查才找到是 if elseif else 代码块的问题&#xff0c;会默认打印输出 else 部分第一个返回值的值&#xff08;下方代码中的 P值&…...

LeetCode/NowCoder-二叉树OJ练习

励志冰檗&#xff1a;形容在清苦的生活环境中激励自己的意志。&#x1f493;&#x1f493;&#x1f493; 目录 说在前面 题目一&#xff1a;单值二叉树 题目二&#xff1a;相同的树 题目三&#xff1a;对称二叉树 题目四&#xff1a;二叉树的前序遍历 题目五&#xff1a;另…...

PSINS工具箱函数介绍——insplot

insplot是一个绘图命令,用于将avp数据绘制出来 本文所述的代码需要基于PSINS工具箱,工具箱的讲解: PSINS初学指导基于PSINS的相关程序设计(付费专题)使用方法 此函数使用起来也很简单,直接后面加avp即可,如: insplot(avp);其中,avp为: 每行表示一个时间1~3列为姿态…...

Docker简单快速入门

1. 安装Docker 基于 Ubuntu 24.04 LTS 安装Docker 。 # 更新包索引并安装依赖包 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common# 添加Docker的官方GPG密钥并存储在正确的位置 curl -fsSL https://mirror…...

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 图像物体的边界(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…...

【无人机】低空经济中5G RedCap芯片的技术分析报告

1. 引言 图一. 新基建&#xff1a;低空经济 低空经济作为一种新兴的经济形态&#xff0c;涵盖了无人机、电动垂直起降飞行器&#xff08;eVTOL&#xff09;、低空物流、空中交通管理等多个领域。随着5G网络的普及和演进&#xff0c;5G RedCap&#xff08;Reduced Capability&a…...

MongoDB教程(二十一):MongoDB大文件存储GridFS

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、GridFS…...

vue 搜索框

效果 创建搜索组件&#xff1a; 在Vue项目中&#xff0c;首先需要创建一个搜索组件。这个组件通常包含一个输入框和一个搜索按钮。使用v-model指令将输入框与组件的数据属性&#xff08;如searchKeyword&#xff09;进行双向绑定&#xff0c;以便获取用户输入的关键词。处理搜索…...

国科大作业考试资料-人工智能原理与算法-2024新编-第五次作业整理

1、本题以井字棋(圈与十字游戏)为例练习博弈中的基本概念。定义X_n为恰好有n个X而没有O 的行、列或者对角线的数目。同样O_n为正好有n 个O的行、列或者对角线的数目。效用函数给 X_3=1的棋局+1, 给O_3=1的棋局-1。所有其他终止状态效用值为0。对于非终止状态,使用线性的 …...

C++五子棋(未做完,但能玩,而且还不错)

代码放下面了&#xff0c;关于步骤介绍的我以后再完善一下。 #include<bits/stdc.h> #include<cstdio> #include<cstdlib> #include<ctime> #include<windows.h> #include<stdlib.h> #include<time.h> #define random(x) (rand()%x…...

二分查找代码详解

二分查找代码实现 以下是完整的代码和解释&#xff1a; #include <stdio.h>int binarySearch(int arr[], int length, int target) {int left 0;int right length - 1;while (left < right) {int mid left (right - left) / 2; // 防止溢出if (arr[mid] target…...

uniapp的h5,读取本地txt带标签的文件

效果图 使用的回显的标签是u-parse&#xff0c;下面的网址讲了这个标签的相关 https://www.cnblogs.com/huihuihero/p/12978903.html 导入此插件 https://ext.dcloud.net.cn/plugin?id364 使用 uni.request({// 本地文件url: "/static/互联网医院医师端用户协议.txt…...

韦东山嵌入式linux系列-具体单板的按键驱动程序(查询方式)

1 GPIO 操作回顾 &#xff08;1&#xff09;使能模块&#xff1b; &#xff08;2&#xff09;设置引脚的模式&#xff08;工作于GPIO模式&#xff09;&#xff1b; &#xff08;3&#xff09;设置GPIO本身&#xff08;输入/输出&#xff09;&#xff1b; &#xff08;4&…...

如何使用 API list 极狐GitLab 群组中的镜像仓库?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

PHP设计模式-简单工厂模式

核心&#xff1a; 一、定义一个接口类里面写规定好的方法。 interface Message{public function send(array $params);public function getMessage(array $params);public function getCode(array $params);} 二、定义产品类 、产品类继承接口类 class AlliYunSms implements …...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...