常见错误与预防
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 20static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];
带日志记录功能的内存分配MallocExt()和内存释放FreeExt()函数:
/********************************************************** * 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;}
/********************************************************* * 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;}