一. ?常見錯誤與預防
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的’?’寫到動態內存外*/ }
?
預防:??分配內存前仔細思考長度是否足夠,千萬注意字符串拷貝占用內存比字符串長度大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??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; }
?
帶日志記錄功能的內存分配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. ",?__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; }
?
審核編輯:湯梓紅
評論
查看更多