嵌入式系统中如何正确使用动态内存?

news2025/1/13 15:42:17

​   大家好,今天给大家分享一下,动态内存的使用方法

一.  常见错误与预防

1.   分配后忘记释放内存

void func(void)
{
    p = malloc(len);
    do_something(p);
    return;  /*错误!退出程序时没有释放内存*/
}

预防:  编写代码时malloc()和free()保证成对出现,避免忘记资源回收。

int func(void)
{
    p = malloc(len);
    if (condition)
        return -1;  /*错误!退出程序时没有释放内存*/
    free(p);
    return 0;
}

预防: 一旦使用动态内存分配,请仔细检查程序的退出分支是否已经释放该动态内存。

2.   释放内存调用错误指针

void func(void)
{
    p = malloc(len);
    val = *p++;  /*错误!动态内存句柄不可移动*/
    free(p);
}

预防: 千万不要修改动态内存句柄!可以另外赋值给其他指针变量,再对该动态内存进行访问操作。

3.   分配内存不够导致溢出

void func(void)
{
    len = strlen(str);
    p = malloc(len);
    strcpy(p, str);  /*错误!str的’\0’写到动态内存外*/
}

预防:  分配内存前仔细思考长度是否足够,千万注意字符串拷贝占用内存比字符串长度大1。

二.  自动查错机制

尽管在开发过程中坚守原则和谨慎编程甚至严格测试,然而内存泄露的错误还是难以杜绝,如何让系统自动查出内存泄露的错误呢?

一种比较好的方法是建立日志块,即每次分配内存时记录该内存块的指针和大小,释放时再去除该日志块,如果有内存泄露就会有对应的日志块记录这些内存没有释放,这样就可以提醒程序员进行查错。

有了上述日志块操作函数,再来实现动态内存分配与释放函数就很容易了。只有当处于DEBUG版本和打开内存调试DMEM_DBG时才进行日志登录,否则MallocExt()和FreeExt()函数与malloc()和free()是等价的,这样保证了系统处于发布版本时的性能。

(代码已经过严格测试,但这不是盈利的商业代码,即没有版权。但如果因代码错误带来的任何损失作者具有免责权利)

代码部分:

首先定义日志块结构体:

/* Log of dynamic memory usage */
typedef struct _dmem_log
{
    struct _dmem_log *p_stNext; /* Point to next log */
    const void *p_vDMem; /* Point to allocated memory by this pointer */
    INT32S iSize; /* Size of the allocated memory */
} DMEM_LOG;

然后为该结构体开辟内存:

static DMEM_LOG *s_pstFreeLog; /* Point to free log pool by this pointer */
static INT8U s_byNumUsedLog;
static DMEM_LOG *s_pstHeadLog; /* Point to used log chain by this pointer */

/* Pool of dynamic memory log */
#define NUM_DMEM_LOG 20
static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];

下面是内存日志块的操作函数:初始化、插入日志和移除日志:

/**********************************************************                                                             *                    Initialize DMem Log
* Description : Initialize log of dynamic memory
* Arguments  : void
* Returns      : void
* Notes        :
**********************************************************/
static void InitDMemLog(void)
{
    INT16S    nCnt;
    /* Initialize pool of log */
    for (nCnt = 0; nCnt < NUM_DMEM_LOG; ++nCnt)
    {
        /* Point to next one */
        s_astDMemLog[nCnt].p_stNext = &s_astDMemLog[nCnt + 1];
    }
    s_astDMemLog[NUM_DMEM_LOG - 1].p_stNext = NULL;
    s_pstFreeLog = &s_astDMemLog[0]; /* Point to the 1th log */
    return;
}

/**********************************************************                                                             *                       Log DMem
* Description : Join an allocated memory into log pool
* Arguments  : const void *p_vAddr    point to address of this allocated memory by this pointer
*             INT32S iSize    size of this allocated memory
* Returns      : void
* Notes        :
**********************************************************/
static void LogDMem(const void *p_vAddr, INT32S iSize)
{
    ASSERT(p_vAddr && iSize > 0);
    DMEM_LOG *p_stLog;
    #if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr;
    #endif
    
    /* Get a log from free pool */
    OS_ENTER_CRITICAL(); /* Avoid race condition on s_pstFreeLog */
    if (!s_pstFreeLog)
    {
        OS_EXIT_CRITICAL();
        PRINTF("Allocate DMemLog failed.\r\n");       
        return;
    }
    p_stLog = s_pstFreeLog;
    s_pstFreeLog = s_pstFreeLog->p_stNext;
    OS_EXIT_CRITICAL();
    
    /* Don't need to protect this log that is free one currently */
    p_stLog->p_vDMem = p_vAddr;
    p_stLog->iSize = iSize;

    /* Put this log into used chain */
    OS_ENTER_CRITICAL(); /* Avoid race condition */
    p_stLog->p_stNext = s_pstHeadLog;
    s_pstHeadLog = p_stLog;
    ++s_byNumUsedLog;
    OS_EXIT_CRITICAL();
    
    return;
}

/**********************************************************                                                             *                       Unlog DMem
* Description : Remove an allocated memory from log pool
* Arguments  : const void *p_vAddr point to address of this allocated memory by this pointer
* Returns      : void
* Notes        :
**********************************************************/
static void UnlogDMem(const void *p_vAddr)
{
    ASSERT(p_vAddr);
    DMEM_LOG    *p_stLog, *p_stPrev;
    #if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr;
    #endif

    /* Search the log */
    OS_ENTER_CRITICAL(); /*Avoid race condition */
    p_stLog = p_stPrev = s_pstHeadLog;
    while (p_stLog)
    {
        if (p_vAddr == p_stLog->p_vDMem)
        {
         break; /* Have found */
        }          

        p_stPrev = p_stLog;        
        p_stLog = p_stLog->p_stNext;    /* Move to next one */
    }
    
    if (!p_stLog)
    {
        OS_EXIT_CRITICAL();
        PRINTF("Search Log failed.\r\n");         
        return;
    }

    /* Remove from used pool */
    if (p_stLog == s_pstHeadLog)
    {
     s_pstHeadLog = s_pstHeadLog->p_stNext;
    }
    else
    {
     p_stPrev->p_stNext = p_stLog->p_stNext;
    }
    --s_byNumUsedLog;
    OS_EXIT_CRITICAL();

    /* Don't need to protect this log that is free one currently */
    p_stLog->p_vDMem = NULL;
    p_stLog->iSize = 0;

    /* Add into free pool */
    OS_ENTER_CRITICAL(); /* Avoid race condition */
    p_stLog->p_stNext = s_pstFreeLog;
    s_pstFreeLog = p_stLog;
    OS_EXIT_CRITICAL();

    return;
}

带日志记录功能的内存分配MallocExt()和内存释放FreeExt()函数:

/*********************************************************                                                    
*                      Malloc Extension
* Description : Malloc a block of memory and log it if need
* Arguments : INT32S iSize    size of desired allocate memory
* Returns: void *NULL= failed, otherwise=pointer of allocated memory
* Notes        :
**********************************************************/
void *MallocExt(INT32S iSize)
{
    ASSERT(iSize > 0);
    void *p_vAddr;

    p_vAddr = malloc(iSize);
    if (!p_vAddr)
    {
     PRINTF("malloc failed at %s line %d.\r\n", __FILE__, __LINE__);
    }
    else
    {
        #if (DMEM_DBG && DBG_VER)
        memset(p_vAddr, 0xA3, iSize); /* Fill gargage for debug */
        LogDMem(p_vAddr, iSize);    /* Log memory for debug */
        #endif
    }

    return p_vAddr;     
}

/**********************************************************
*                      Free Extension
* Description : Free a block of memory and unlog it if need
* Arguments  : void * p_vMem point to the memory by this pointer
* Returns      : void
* Notes        :
**********************************************************/
void FreeExt(void *p_vMem)
{
    ASSERT(p_vMem);

    free(p_vMem);  
    #if (DMEM_DBG && DBG_VER)
    UnlogDMem(p_vMem);    /* Remove memory from log */
    #endif
    return;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1058029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DevExpress ChartControl 画间断线

效果如下&#xff1a; 解决办法&#xff1a;数据源间断位置加入double.NaN demo下载

Linux 下如何调试代码

debug 和 release 在Linux下的默认模式是什么&#xff1f; 是release模式 那你怎么证明他就是release版本? 我们知道如果一个程序可以被调试&#xff0c;那么它一定是debug版本&#xff0c;如果它是release版本&#xff0c;它是没法被调试的&#xff0c;所以说我们可以来调试一…

基于SpringBoot+MyBatis实现的个人博客系统(一)

这篇主要讲解一下如何基于SpringBoot和MyBatis技术实现一个简易的博客系统(前端页面主要是利用CSS,HTML进行布局书写),前端的静态页面代码可以直接复制粘贴,后端的接口以及前端发送的Ajax请求需要自己书写. 博客系统需要完成的接口: 注册登录博客列表页展示博客详情页展示发布博…

【重拾C语言】二、顺序程序设计(基本符号、数据、语句、表达式、顺序控制结构、数据类型、输入/输出操作)

目录 前言 二、顺序程序设计 2.1 求绿化带面积——简单程序 2.2基本符号&#xff1a; 2.2.1 字符集 可视字符 不可视字符 2.2.2 C特定符 关键字 分隔符 运算符 2.2.3 标识符 2.2.4 间隔符 2.2.5 注释 2.3 数据 2.3.1 字面常量&#xff08;Literal Constants&am…

Flutter+SpringBoot实现ChatGPT流实输出

FlutterSpringBoot实现ChatGPT流式输出、上下文了连续对话 最终实现Flutter的流式输出上下文连续对话。 这里就是提供一个简单版的工具类和使用案例&#xff0c;此处页面仅参考。 服务端 这里直接封装提供工具类&#xff0c;修改自己的apiKey即可使用&#xff0c;支持连续…

FOC程序cubemx配置-ADC配置

具体配置步骤大家参考&#xff1a;这篇文章 我配置后用keil5自带的仿真工具查看引脚波形&#xff0c;在这里写一下遇到的问题。 1、波形仿真的时候出现 Unknown Signal&#xff1a;参考 这篇文章 2、生成的波形并不完全互补。 PS&#xff1a;出现以上这种情况时&#xff0…

【斗罗大陆2】动画新增12集备案,冰碧帝皇蝎形象被吐槽遭狂喷!

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析斗罗大陆2绝世唐门。 《斗罗大陆2》动画新增12集备案 《斗罗大陆2》动画正在如火如荼的上映着&#xff0c;《斗罗大陆2》动画也在同步新增了。 在2023年9月全国重点网络动画片规划备案通过剧目信息中&#xff0c;《斗罗大…

【计算机网络】高级IO之select

文章目录 1. 什么是IO&#xff1f;什么是高效 IO? 2. IO的五种模型五种IO模型的概念理解同步IO与异步IO整体理解 3. 阻塞IO4. 非阻塞IOsetnonblock函数为什么非阻塞IO会读取错误&#xff1f;对错误码的进一步判断检测数据没有就绪时&#xff0c;返回做一些其他事情完整代码myt…

【算法|动态规划No.8】leetcode面试题 17.16. 按摩师

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

Python使用词云图展示

网上看到一个txt文本信息&#xff0c;共2351条饭否记录&#xff0c;据说是微信之父每天发的饭否记录&#xff0c;其实我不知道什么是饭否。我读取这个文本内容&#xff0c;展示到词语图上。之前也使用过&#xff0c;但是好久没有玩Python了&#xff0c;称假期空闲&#xff0c;练…

【从入门到起飞】IO高级流(1)(缓冲流,转换流,序列化流,反序列化流)

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;天行健&#xff0c;君子以自强不息。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f384;缓冲流&#x1f354;字节缓冲流&#x1f6f8;一次读取…

vue ant 隐藏列

vue ant 隐藏列 重要代码 type: FormTypes.hidden{ title: 序号, key: barCode, width: 10%, type: FormTypes.hidden},

YTM32的电源管理与低功耗系统详解

YTM32的电源管理与低功耗系统详解 苏勇&#xff0c;2023年10月 文章目录 YTM32的电源管理与低功耗系统详解缘起原理与机制电源管理模型的功耗模式正常模式&#xff08;Normal&#xff09;休眠模式&#xff08;Sleep&#xff09;深度休眠模式&#xff08;DeepSleep&#xff09;…

树概念及结构

.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结点&#xff0c;称为根结点&a…

新手学习笔记-----编译和链接

目录 1. 翻译环境和运⾏环境 2. 翻译环境&#xff1a;预编译编译汇编链接 2.1 预处理 2.2 编译 2.2.1 词法分析 2.2.2 语法分析 2.2.3 语义分析 2.3 汇编 2.4 链接 3. 运⾏环境 1. 翻译环境和运⾏环境 在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第…

【Leetcode】 131. 分割回文串

给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 示例 1&#xff1a; 输入&#xff1a;s "aab" 输出&#xff1a;[["a","a"…

华为云云耀云服务器L实例评测|Huawei Cloud EulerOS 自动化环境部署

[toc] Huawei Cloud EulerOS 自动化环境部署 云耀云服务器L实例【Huawei Cloud EulerOS 2.0 64bit】 Python Git Google Chrome Chromedriver Selenium More… 1. Python 镜像创建后自带。 2.Git 拉取项目。 sudo yum install git3. Google Chrome 使用root权限或sudo权…

WinFroms基于三层构造设计模式的框架所设计的代码生成器1.0

软件开发模式——三层架构 此文章需要在读懂 以上这篇架构模式的基础上再继续往下深入学习简化 目录 1.前言 2.框架准备 3 .coboBox的数据绑定 4.创建文件夹 5.工具方法 6.生成一个数据库访问助手类SqlHelper 7.生成模型层Model 8.生成数据访问层DAL层 9.生成业务…

VD6283TX环境光传感器驱动开发(3)----测试闪烁频率代码

VD6283TX环境光传感器驱动开发----3.测试闪烁频率代码 概述视频教学样品申请源码下载参考代码开发板设置测试结果 概述 ST提供了6283A1_AnalogFlicker代码在X-NUCLEO-6283A1获取闪烁频率&#xff0c;同时移植到VD6283TX-SATEL。 闪烁频率提取主要用于检测光源的闪烁频率&#…

在pycharm中出现下载软件包失败的解决方法

一. 一般情况下我们会选择在设置中下载软件包,过程如下. 1. 直接点击左上角的文件, 再点击设置, 再点击项目, 在右边选择python解释器,点击号,输入要下载的软件包, 在下面的一系列的包中选择相对应的包,点击安装就可以了,有的时候我们下载的是最新的版本,如果要下载固定的版本…