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

Posted 嵌入式大杂烩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式中如何正确使用动态内存?相关的知识,希望对你有一定的参考价值。

👇 星标「嵌入式大杂烩」,一起进步!👇

一.  常见错误与预防

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;

原文链接:https://blog.csdn.net/jiangjunjie_2005/article/details/26937879

本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

以上是关于嵌入式中如何正确使用动态内存?的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式Linux C语言——指针

vectorの奇技淫巧C++ vector 如何正确处理动态申请内存的元素

❌❌vectorの奇技淫巧⭕⭕C++ vector 如何正确处理动态申请内存的元素

如何在MD(d)和MT(d)工程间正确分配和释放动态内存

如何正确地为字符串“数组”动态分配内存

没有动态内存分配的 Lex 和 Yacc