一. ?常見錯誤與預防
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)存。
2. ? 釋放內(nèi)存調(diào)用錯誤指針
void?func(void)
{
????p?=?malloc(len);
????val?=?*p++;??/*錯誤!動態(tài)內(nèi)存句柄不可移動*/
????free(p);
}
預防:?千萬不要修改動態(tài)內(nèi)存句柄!可以另外賦值給其他指針變量,再對該動態(tài)內(nèi)存進行訪問操作。
3. ? 分配內(nèi)存不夠?qū)е乱绯?/p>
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??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











評論