C语言释放自定义String结构体时出现Double Free Detected错误如何解决
问题根源&修复方案
你遇到的double free报错本质不是String_Dispose本身的逻辑错误,而是其他函数的实现不符合内存管理规则,导致传入free的指针不属于动态堆内存、或者出现重复释放的情况,核心错误点如下:
1. String_Create是直接诱因
你的String_Create实现直接把传入的源字符串指针赋值给了str->pData:
str->pData = pSrc;
而你调用时传入的是字符串常量"And as we wind on down the road...",这类常量存储在程序的静态常量区,不是malloc动态分配的内存,调用String_Dispose时执行free(pData)属于非法释放非堆内存,会破坏内存分配器的tcache结构,最终触发double free报错。
同时String_Create还有两处错误:
- 内存分配失败时返回
-1(整数),不符合返回NULL的约定 - 没有按照注释要求复制源字符串内容,违反了
S.pData != pSrc->pData的后置条件
修复方案:
String* String_Create(const char* const pSrc, uint32_t slength) { if (pSrc == NULL || slength == 0) { return NULL; } String* str = (String*)malloc(sizeof(String)); if (str == NULL) { printf("Failed to allocate memory."); return NULL; } // 动态分配pData内存 str->pData = (char*)malloc(slength + 1); if (str->pData == NULL) { free(str); printf("Failed to allocate memory."); return NULL; } // 复制字符串,手动补结束符 strncpy(str->pData, pSrc, slength); str->pData[slength] = '\0'; // 按注释要求处理提前遇到'\0'的情况,计算实际长度 str->length = strlen(str->pData); return str; }
2. 其他函数存在的关联错误
这些错误也可能间接引发内存问题:
String_Cat的第二个循环写错了源指针,当前是复制pDest->pData两次,应该改成strCmb[i++] = pSrc->pData[j++];;同时替换pDest->pData之前要先释放原来的pData,避免内存泄漏String_subString的循环条件错误,应该是i < start + length而不是i < length;给字符串加结束符应该写strData[j] = '\0',你当前写的"\0"是字符串指针,属于类型错误,同时下标偏移多了1- 所有内存分配失败的分支都统一返回
NULL,不要返回-1
3. String_Dispose增加防御性判断
增加空指针判断可以避免重复调用释放函数导致的double free:
void String_Dispose(String** ppStr) { // 前置判断,避免空指针或已释放的指针传入 if (ppStr == NULL || *ppStr == NULL) { return; } free((*ppStr)->pData); free(*ppStr); *ppStr = NULL; }
后续排查方向
- 检查业务逻辑中是否存在浅拷贝
String结构体的情况:比如两个String*指针指向同一个_String结构体实例,分别调用String_Dispose会触发重复释放 - 编译时添加
-g -fsanitize=address参数,使用AddressSanitizer工具运行程序,可以直接定位到非法内存操作的准确位置,比原生gdb排查效率更高
内容的提问来源于stack exchange,提问作者Azure21
相关产品推荐
相关产品推荐

