常見錯誤與預防
1 分配后忘記釋放內(nèi)存
void func(void)
{
p = malloc(len);
do_something(p);
return; /*錯誤!退出程序時沒有釋放內(nèi)存*/
}
????預防:編寫代碼時malloc()和free()保證成對出現(xiàn),避免忘記資源回收。
int func(void)
{
p = malloc(len);
if (condition)
return -1; /*錯誤!退出程序時沒有釋放內(nèi)存*/
free(p);
return 0;
}
????預防:一旦使用動態(tài)內(nèi)存分配,請仔細檢查程序的退出分支是否已經(jīng)釋放該動態(tài)內(nèi)存。
void func(void)
{
p = malloc(len);
val = *p++; /*錯誤!動態(tài)內(nèi)存句柄不可移動*/
free(p);
}
????預防:千萬不要修改動態(tài)內(nèi)存句柄!可以另外賦值給其他指針變量,再對該動態(tài)內(nèi)存進行訪問操作。
void func(void)
{
len = strlen(str);
p = malloc(len);
strcpy(p, str); /*錯誤!str的’’寫到動態(tài)內(nèi)存外*/
}
????預防:分配內(nèi)存前仔細思考長度是否足夠,千萬注意字符串拷貝占用內(nèi)存比字符串長度大1。? 自動查錯機制 ?
???盡管在開發(fā)過程中堅守原則和謹慎編程甚至嚴格測試,然而內(nèi)存泄露的錯誤還是難以杜絕,如何讓系統(tǒng)自動查出內(nèi)存泄露的錯誤呢? ????一種比較好的方法是建立日志塊,即每次分配內(nèi)存時記錄該內(nèi)存塊的指針和大小,釋放時再去除該日志塊,如果有內(nèi)存泄露就會有對應的日志塊記錄這些內(nèi)存沒有釋放,這樣就可以提醒程序員進行查錯。 ???
?有了上述日志塊操作函數(shù),再來實現(xiàn)動態(tài)內(nèi)存分配與釋放函數(shù)就很容易了。只有當處于DEBUG版本和打開內(nèi)存調(diào)試DMEM_DBG時才進行日志登錄,否則MallocExt()和FreeExt()函數(shù)與malloc()和free()是等價的,這樣保證了系統(tǒng)處于發(fā)布版本時的性能。 ????代碼已經(jīng)過嚴格測試,但這不是盈利的商業(yè)代碼,即沒有版權(quán)。但如果因代碼錯誤帶來的任何損失作者具有免責權(quán)利。 ????代碼部分: ????首先定義日志塊結(jié)構(gòu)體:
/* 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;? ????然后為該結(jié)構(gòu)體開辟內(nèi)存:
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];? ????下面是內(nèi)存日志塊的操作函數(shù):初始化、插入日志和移除日志:
/********************************************************** * 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.
");
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.
");
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;
}
? ????帶日志記錄功能的內(nèi)存分配MallocExt()和內(nèi)存釋放FreeExt()函數(shù):
/*********************************************************
* 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.
", __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;
}
? 編輯:黃飛
?
?
?
電子發(fā)燒友App












評論