(pBuf);
6?
7 ? ? return 0;
8 }
9 //执行后报错:*** glibc detected *** ./test: free(): invalid pointer: 0xbf84b35c ***
复制代码
? ? ?情况2多发生在从申请内存到最后释放跨越多个模块历经大量处理逻辑时,指针初始值被修改掉。简单示例如下:
?
复制代码
?1 int main(void)
?2 {
?3 ? ? char *pMem = malloc(10);
?4 ? ? if(NULL == pMem)
?5 ? ? ? ? return -1;?
?6?
?7 ? ? pMem++;
?8 ? ? free(pMem);
?9?
10 ? ? return 0;
11 }
12 //执行后报错:*** glibc detected *** ./test: free(): invalid pointer: 0x082b5009 ***
复制代码
? ? ?内存越界也可能导致内存释放失败:
?
复制代码
?1 int main(void)
?2 {
?3 ? ? char *pMem = malloc(2);
?4 ? ? if(NULL == pMem)
?5 ? ? ? ? return -1;?
?6?
?7 ? ? memset(pMem, 0, sizeof(int)*10);
?8 ? ? free(pMem);
?9 ? ? return 0;
10 }
11 //执行后报错:*** glibc detected *** ./test: free(): invalid next size (fast): 0x09efa008 ***
复制代码
? ? ?内存重复释放最简单但最不可能出现的示例如下:
?
复制代码
?1 int main(void)
?2 {
?3 ? ? char *pMem = malloc(10);
?4 ? ? if(NULL == pMem)
?5 ? ? ? ? return -1;?
?6?
?7 ? ? free(pMem);
?8 ? ? free(pMem);
?9?
10 ? ? return 0;
11 }
12 //执行后报错:*** glibc detected *** ./test: double free or corruption (fasttop): 0x09709008 ***
复制代码
? ? ?通常,编码者会封装接口以更好地管理内存的申请和释放。若释放接口内部在释放前未判断指向动态内存的指针是否为空,或释放后未将指向该内存的指针设置为空。当程序中调用关系或处理逻辑过于复杂(尤其是对于全局性的动态内存),难以搞清内存何时或是否释放,加之接口未作必要的防护,极易出现内存重复释放。
?
? ? ?此外,当程序中存在多份动态内存指针的副本时,很容易经由原内存指针及其副本释放同一块内存。
?
复制代码
?1 int main(void)
?2 {
?3 ? ? char *pMem = malloc(sizeof(char)*10);
?4 ? ? if(NULL == pMem)
?5 ? ? ? ? return -1;
?6?
?7 ? ? char *pMemTemp = pMem;
?8 ? ? //Do Something...
?9?
10 ? ? free(pMem);
11 ? ? free(pMemTemp);
12 ? ? return 0;
13 }
复制代码
? ? ?上例中仅需释放pMem或pMemTemp其一即可。
?
? ? 【对策】幸运的是,内存释放失败会导致程序崩溃,故障明显。并且,可借助静态或动态的内存检测技术进行排查。
?
? ? ?对于重复释放,可仿照《
C语言通用双向循环链表操作函数集》一文中介绍的SAFE_FREE宏,尽可能地“规避”其危害(但当内存指针存在多个副本时无能为力)。
?
复制代码
1 #define SAFE_FREE(pointer) ? SafeFree(&(pointer)) ?//与SAFE_ALLOC入参指针形式一致
2 void SafeFree(void **pointer)
3 {
4 ? ? if(pointer != NULL)
5 ? ? {
6 ? ? ? ? free(*pointer);
7 ? ? ? ? *pointer = NULL;
8 ? ? }
9 }
复制代码
? ? ?此外,应在设计阶段保证数据结构和流程尽量地简洁合理,从根本上解决对象管理的混乱。
?
2.3.4 内存分配与释放不配对
? ? ?编码者一般能保证malloc和free配对使用,但可能调用不同的实现。例如,同样是free接口,其调试版与发布版、单线程库与多线程库的实现均有所不同。一旦链接错误的库,则可能出现某个内存管理器中分配的内存,在另一个内存管理器中释放的问题。此外,模块封装的内存管理接口(如GetBuffer和FreeBuffer)在使用时也可能出现GetBuffer配free,或malloc配FreeBuffer的情况,尤其是跨函数的动态内存使用。
?
? ? 【对策】动态内存的申请与释放接口调用方式和次数必须配对,防止内存泄漏。分配和释放最好由同一方管理,并提供专门的内存管理接口。
?
2.3.5 内存越界
? ? ?除明显的读写越界外,关于动态内存还存在一种sizeof计算错误导致的越界:
?
复制代码
?1 int main(void)
?2 {
?3 ? ? T_CHK_MEM *pMem = malloc(sizeof(pMem));
?4 ? ? if(NULL == pMem)
?5 ? ? ? ? return -1;
?6?
?7 ? ? memset(pMem, 0, sizeof(T_CHK_MEM));
?8 ? ? free(pMem);
?9 ? ? return 0;
10 }
11 //执行后报错:*** glibc detected *** ./test: free(): invalid next size (fast): 0x09239008 ***
复制代码
? ? ?这种越界也是内存释放失败的一个原因。正确的内存申请写法应该是:
?
1 T_CHK_MEM *pMem = malloc(sizeof(*pMem));
2 //Or
3 T_CHK_MEM *pMem = malloc(sizeof(T_CHK_MEM));