为何给char缓冲区末尾赋值空终止符后无法释放内存?
赋值空终止符后free报错的原因及修复
核心问题:内存越界访问
你只给缓冲区分配了length字节的内存,而数组下标从0开始,所以缓冲区的有效访问范围是buffer[0]到buffer[length-1]。buffer[length] = '\0';这行代码直接写入了超出分配范围的内存位置,属于未定义行为——这种操作会破坏内存管理的内部结构,等到free时系统检测到内存块被非法修改,就会触发报错。
为什么不加这行时free能正常执行?因为没触碰越界内存,内存管理的元数据没被破坏,所以free可以正常完成(但此时用printf("%s", t)输出字符串存在风险:没有终止符的情况下,程序会一直读取内存直到遇到随机的\0,可能输出乱码或触发其他问题)。
修复方案
分配内存时多预留1字节用于存储空终止符:
buffer = malloc((size_t)length + 1);
这样buffer[length]就属于合法的内存范围,赋值\0后再free就不会报错。
原代码的其他隐患
除了内存越界,原代码还有几个容易导致崩溃的问题:
- fopen失败后没有终止函数,后续会继续执行fseek、ftell等操作,直接操作NULL指针导致崩溃
- malloc失败后没有终止函数,后续操作NULL指针也会出问题
- ftell返回的
long类型和malloc要求的size_t类型不匹配,需要做类型转换避免溢出风险 - fread没有校验读取的字节数,可能出现文件未完全读取的情况
修复后的完整代码
char *readFileToString(const char *file) { char *buffer = NULL; long length; FILE *f = fopen(file, "rb"); if (f == NULL) { printf("ERROR::FILE:FILE_NOT_SUCCESFULLY_READ\n"); return NULL; } if (fseek(f, 0, SEEK_END) != 0) { printf("ERROR::FILE:FAILED_TO_SEEK_END\n"); fclose(f); return NULL; } length = ftell(f); if (length == -1) { printf("ERROR::FILE:FAILED_TO_GET_FILE_LENGTH\n"); fclose(f); return NULL; } if (fseek(f, 0, SEEK_SET) != 0) { printf("ERROR::FILE:FAILED_TO_SEEK_START\n"); fclose(f); return NULL; } // 多分配1字节用于存储空终止符 buffer = malloc((size_t)length + 1); if (buffer == NULL) { printf("Error allocating buffer memory\n"); fclose(f); return NULL; } size_t read_bytes = fread(buffer, 1, (size_t)length, f); if (read_bytes != (size_t)length) { printf("ERROR::FILE:FAILED_TO_READ_FULL_CONTENTS\n"); free(buffer); fclose(f); return NULL; } fclose(f); buffer[length] = '\0'; // 合法访问,不会破坏内存结构 return buffer; } int main() { char *t = readFileToString("test"); if (t != NULL) { printf("%s", t); free(t); // 正常释放无报错 } return 0; }
内容的提问来源于stack exchange,提问作者Aidan
相关产品推荐
相关产品推荐

