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


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

    一.  常见错误与预防

    1.   分配后忘记释放内存

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

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

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

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

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

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

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

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

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

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

    二.  自动查错机制

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

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

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

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

    代码部分:

    首先定义日志块结构体:

    1. /* Log of dynamic memory usage */
    2. typedef struct _dmem_log
    3. {
    4.     struct _dmem_log *p_stNext; /* Point to next log */
    5.     const void *p_vDMem; /* Point to allocated memory by this pointer */
    6.     INT32S iSize; /* Size of the allocated memory */
    7. } DMEM_LOG;

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

    1. static DMEM_LOG *s_pstFreeLog; /* Point to free log pool by this pointer */
    2. static INT8U s_byNumUsedLog;
    3. static DMEM_LOG *s_pstHeadLog; /* Point to used log chain by this pointer */
    4. /* Pool of dynamic memory log */
    5. #define NUM_DMEM_LOG 20
    6. static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];

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

    1. /**********************************************************                                                             *                    Initialize DMem Log
    2. * Description : Initialize log of dynamic memory
    3. * Arguments  : void
    4. * Returns      : void
    5. * Notes        :
    6. **********************************************************/
    7. static void InitDMemLog(void)
    8. {
    9.     INT16S    nCnt;
    10.     /* Initialize pool of log */
    11.     for (nCnt = 0; nCnt < NUM_DMEM_LOG; ++nCnt)
    12.     {
    13.         /* Point to next one */
    14.         s_astDMemLog[nCnt].p_stNext = &s_astDMemLog[nCnt + 1];
    15.     }
    16.     s_astDMemLog[NUM_DMEM_LOG - 1].p_stNext = NULL;
    17.     s_pstFreeLog = &s_astDMemLog[0]; /* Point to the 1th log */
    18.     return;
    19. }
    20. /**********************************************************                                                             *                       Log DMem
    21. * Description : Join an allocated memory into log pool
    22. * Arguments  : const void *p_vAddr    point to address of this allocated memory by this pointer
    23. *             INT32S iSize    size of this allocated memory
    24. * Returns      : void
    25. * Notes        :
    26. **********************************************************/
    27. static void LogDMem(const void *p_vAddr, INT32S iSize)
    28. {
    29.     ASSERT(p_vAddr && iSize > 0);
    30.     DMEM_LOG *p_stLog;
    31.     #if OS_CRITICAL_METHOD == 3
    32.     OS_CPU_SR  cpu_sr;
    33.     #endif
    34.     
    35.     /* Get a log from free pool */
    36.     OS_ENTER_CRITICAL(); /* Avoid race condition on s_pstFreeLog */
    37.     if (!s_pstFreeLog)
    38.     {
    39.         OS_EXIT_CRITICAL();
    40.         PRINTF("Allocate DMemLog failed.\r\n");       
    41.         return;
    42.     }
    43.     p_stLog = s_pstFreeLog;
    44.     s_pstFreeLog = s_pstFreeLog->p_stNext;
    45.     OS_EXIT_CRITICAL();
    46.     
    47.     /* Don't need to protect this log that is free one currently */
    48.     p_stLog->p_vDMem = p_vAddr;
    49.     p_stLog->iSize = iSize;
    50.     /* Put this log into used chain */
    51.     OS_ENTER_CRITICAL(); /* Avoid race condition */
    52.     p_stLog->p_stNext = s_pstHeadLog;
    53.     s_pstHeadLog = p_stLog;
    54.     ++s_byNumUsedLog;
    55.     OS_EXIT_CRITICAL();
    56.     
    57.     return;
    58. }
    59. /**********************************************************                                                             *                       Unlog DMem
    60. * Description : Remove an allocated memory from log pool
    61. * Arguments  : const void *p_vAddr point to address of this allocated memory by this pointer
    62. * Returns      : void
    63. * Notes        :
    64. **********************************************************/
    65. static void UnlogDMem(const void *p_vAddr)
    66. {
    67.     ASSERT(p_vAddr);
    68.     DMEM_LOG    *p_stLog, *p_stPrev;
    69.     #if OS_CRITICAL_METHOD == 3
    70.     OS_CPU_SR  cpu_sr;
    71.     #endif
    72.     /* Search the log */
    73.     OS_ENTER_CRITICAL(); /*Avoid race condition */
    74.     p_stLog = p_stPrev = s_pstHeadLog;
    75.     while (p_stLog)
    76.     {
    77.         if (p_vAddr == p_stLog->p_vDMem)
    78.         {
    79.          break; /* Have found */
    80.         }          
    81.         p_stPrev = p_stLog;        
    82.         p_stLog = p_stLog->p_stNext;    /* Move to next one */
    83.     }
    84.     
    85.     if (!p_stLog)
    86.     {
    87.         OS_EXIT_CRITICAL();
    88.         PRINTF("Search Log failed.\r\n");         
    89.         return;
    90.     }
    91.     /* Remove from used pool */
    92.     if (p_stLog == s_pstHeadLog)
    93.     {
    94.      s_pstHeadLog = s_pstHeadLog->p_stNext;
    95.     }
    96.     else
    97.     {
    98.      p_stPrev->p_stNext = p_stLog->p_stNext;
    99.     }
    100.     --s_byNumUsedLog;
    101.     OS_EXIT_CRITICAL();
    102.     /* Don't need to protect this log that is free one currently */
    103.     p_stLog->p_vDMem = NULL;
    104.     p_stLog->iSize = 0;
    105.     /* Add into free pool */
    106.     OS_ENTER_CRITICAL(); /* Avoid race condition */
    107.     p_stLog->p_stNext = s_pstFreeLog;
    108.     s_pstFreeLog = p_stLog;
    109.     OS_EXIT_CRITICAL();
    110.     return;
    111. }

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

    1. /*********************************************************                                                    
    2. *                      Malloc Extension
    3. * Description : Malloc a block of memory and log it if need
    4. * Arguments : INT32S iSize    size of desired allocate memory
    5. * Returns: void *NULL= failed, otherwise=pointer of allocated memory
    6. * Notes        :
    7. **********************************************************/
    8. void *MallocExt(INT32S iSize)
    9. {
    10.     ASSERT(iSize > 0);
    11.     void *p_vAddr;
    12.     p_vAddr = malloc(iSize);
    13.     if (!p_vAddr)
    14.     {
    15.      PRINTF("malloc failed at %s line %d.\r\n", __FILE__, __LINE__);
    16.     }
    17.     else
    18.     {
    19.         #if (DMEM_DBG && DBG_VER)
    20.         memset(p_vAddr, 0xA3, iSize); /* Fill gargage for debug */
    21.         LogDMem(p_vAddr, iSize);    /* Log memory for debug */
    22.         #endif
    23.     }
    24.     return p_vAddr;     
    25. }
    26. /**********************************************************
    27. *                      Free Extension
    28. * Description : Free a block of memory and unlog it if need
    29. * Arguments  : void * p_vMem point to the memory by this pointer
    30. * Returns      : void
    31. * Notes        :
    32. **********************************************************/
    33. void FreeExt(void *p_vMem)
    34. {
    35.     ASSERT(p_vMem);
    36.     free(p_vMem);  
    37.     #if (DMEM_DBG && DBG_VER)
    38.     UnlogDMem(p_vMem);    /* Remove memory from log */
    39.     #endif
    40.     return;
    41. }
  • 相关阅读:
    BUG系列路径规划算法原理介绍(五)——RandomBug算法
    【Java实战】大厂都是怎样进行单元测试的
    什么是堆栈和队列?如何实现它们?
    git工作使用
    OpenFeign源码1-环境搭建及核心类说明
    【云原生-白皮书】简章2:深入理解DevOps+微服务
    java集合
    智能文件改名:高效复制并删除冗余,简化文件管理“
    c++ 11 原子操作库 (std::atomic)(二)
    Python数据类型:列表的魔法世界
  • 原文地址:https://blog.csdn.net/weixin_41114301/article/details/133523109