移除else块后free()触发错误的原因是什么?
问题分析与修复
你遇到的核心问题是返回栈局部变量的地址导致的野指针访问,以下是具体分析和修复方案:
原代码
sedex.h
#ifndef KAAL_SEDEX_H #define KAAL_SEDEX_H #include "limits.h" #ifdef ULLONG_MAX #include "assert.h" #include "crtdbg.h" #include "stdio.h" #include "stdlib.h" #define MEMORY_UNINITIALIZED (size_t)(0xCCCCCCCCCCCCCCCC) #define MEMORY_AVAILABLE(ptr) (assert(NULL != ptr && MEMORY_UNINITIALIZED != (size_t)(ptr))) typedef char* string; typedef long long llong; typedef enum { true, false } bool; typedef struct sedecimstruct { size_t length; llong* content; } *sedex; sedex CreateSedex(string source); void DeleteSedex(sedex target); #endif #endif
sedex.c
#include "sedex.h" int main(void) { sedex n = CreateSedex("+"); DeleteSedex(n); _CrtDumpMemoryLeaks(); return 0; } sedex CreateSedex(string source) { struct sedecimstruct output; bool sgned = source[0] == '+' || source[0] == '-'; if (source == NULL || strlen(source) == 0 || strlen(source) == 1 && sgned) { output.length = 2; output.content = calloc(output.length, sizeof(llong)); } else { size_t digits = strlen(source) - sgned, mod = digits & 0xf; bool excess = mod > 0; output.length = (digits >> 4) + excess + 1; output.content = malloc(output.length * sizeof(llong)); output.content[0] = source[0] == '-'; char sedecim[17] = { 0 }; MEMORY_AVAILABLE(output.content); for (size_t i = 1; i <= digits; i++) { sedecim[(i - 1) & 0xf] = source[i - 1 + sgned]; if ((i & 0xf) == 0) { output.content[i >> 4] = atoll(sedecim); memset(sedecim, 0, sizeof(sedecim)); continue; } if (i == digits && excess) { output.content[output.length - 1] = atoll(sedecim); memset(sedecim, 0, sizeof(sedecim)); } } } return &output; } void DeleteSedex(sedex target) { free(target->content); // breakpoint triggers here when else is empty or removed target->content = target->length = NULL; }
问题根源
- 野指针的产生:
CreateSedex函数里的struct sedecimstruct output是栈上的局部变量,函数执行完毕返回时,栈帧会被销毁,返回的&output变成指向无效内存的野指针。 - else块存在时的侥幸正常:当else块有内容时,函数执行过程中栈帧占用的空间更大,返回后栈内存被覆盖的速度较慢,野指针刚好还能访问到原来的内存区域,属于未定义行为的“巧合”;当else块被清空或移除后,函数执行完栈帧立即被后续操作覆盖,野指针指向的内存已经完全无效,调用
free(target->content)时就会触发调试断点。
修复方案
把结构体从栈分配改为堆分配,确保返回的地址在函数结束后仍然有效:
修改后的CreateSedex函数
sedex CreateSedex(string source) { // 堆分配结构体实例,确保返回后内存有效 sedex output = malloc(sizeof(struct sedecimstruct)); if (!output) { perror("malloc failed"); return NULL; } bool sgned = source[0] == '+' || source[0] == '-'; if (source == NULL || strlen(source) == 0 || strlen(source) == 1 && sgned) { output->length = 2; output->content = calloc(output->length, sizeof(llong)); } else { size_t digits = strlen(source) - sgned, mod = digits & 0xf; bool excess = mod > 0; output->length = (digits >> 4) + excess + 1; output->content = malloc(output->length * sizeof(llong)); if (!output->content) { perror("malloc content failed"); free(output); // 结构体已分配,要先释放避免泄漏 return NULL; } output->content[0] = source[0] == '-'; char sedecim[17] = { 0 }; MEMORY_AVAILABLE(output->content); for (size_t i = 1; i <= digits; i++) { sedecim[(i - 1) & 0xf] = source[i - 1 + sgned]; if ((i & 0xf) == 0) { output->content[i >> 4] = atoll(sedecim); memset(sedecim, 0, sizeof(sedecim)); continue; } if (i == digits && excess) { output->content[output->length - 1] = atoll(sedecim); memset(sedecim, 0, sizeof(sedecim)); } } } return output; }
修改后的DeleteSedex函数
void DeleteSedex(sedex target) { if (target) { free(target->content); // 先释放content的堆内存 free(target); // 再释放结构体本身的堆内存 } }
内容的提问来源于stack exchange,提问作者amegyoushi
相关产品推荐
相关产品推荐

