常見錯誤與預防
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 < 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; }? ????帶日志記錄功能的內存分配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; }? 編輯:黃飛
?
?
?
評論