打印字符串后释放dlmfnt内存为何导致C程序输出异常?
问题:调用
free(dlmfnt)后输出乱码,不释放内存则正常 背景信息
需求为读取文件内容,通过自定义dtok_r函数解析字符串并原样打印。dtok.c实现解析逻辑,main.c负责文件读取与解析函数调用。
目标文件内容
One Fish Two 2 Red 2 Blue 2 Dr. Seuss 7 fish, 8 2, 8 2, 8 2, Black 2, 3 2, Old 2, New 2. This one has a little car. 6 6 6 6 6 star. Say! What 5 lot of 12 there are. Yes. Some 3 red, and some 4 blue. 6 3 old 6 6 4 new. 6 3 sad, 6 6 4 glad, And 4 4 very, 1 bad. Why 4 they 10 10 10 2 7? 0
dtok.c代码
#include <string.h> #include <malloc.h> #include <assert.h> char *dtok_r(char *s, const char *delim, char **save_ptr, char **dlmfnt, char *dlmbck) { *dlmfnt = NULL; *dlmbck = '\0'; char *end; if (s == NULL) s = *save_ptr; if (*s == '\0') { *save_ptr = s; return NULL; } /* Scan the length of leading delimiter str. */ size_t dlmfnt_len = strspn(s, delim); if (dlmfnt_len > 0) { *dlmfnt = (char *)malloc((dlmfnt_len + 1) * sizeof(char)); assert(dlmfnt != NULL && "NOT ENOUGH MEMORY FOR dlmfnt!!!"); strncpy(*dlmfnt, s, dlmfnt_len); *(dlmfnt)[dlmfnt_len] = '\0'; } s += dlmfnt_len; if (*s == '\0') { *save_ptr = s; return NULL; } /* Find the end of the token. */ end = s + strcspn(s, delim); if (*end == '\0') { *save_ptr = end; return s; } /* Terminate the token and make *SAVE_PTR point past it and assign dlmbck */ if (*end != '\n') { *dlmbck = *end; } *end = '\0'; *save_ptr = end + 1; return s; }
main.c代码
/* Include standard library headers */ #include <stdio.h> #include <string.h> #include <inttypes.h> #include <stdlib.h> #include <assert.h> #include <stddef.h> /* System Constants */ #define MAX_LINE_SIZE 512 int main(int argc, char **argv) { assert(argc == 2 && \ "Program only operates with one file" ); /* Open the file to decompress */ FILE *dcomp_file = fopen(argv[1], "r"); assert(dcomp_file != NULL && \ "Error opening file -->possibly missing<--" ); /* Line Buffer for fgets */ char line_buff[MAX_LINE_SIZE]; /* dtok_r variable inits */ char *word_token, *save_ptr, dlmbck; static const char *delim = " \n,.?!'\":;-"; /* Delimiter string for dtok_r */ /* Read line from file until end of file */ while (((fgets(line_buff, MAX_LINE_SIZE, dcomp_file) != NULL) && \ (strcmp(line_buff, "0\n") != 0))) { save_ptr = line_buff; if (strcmp(line_buff, "\n") != 0) { char *dlmfnt = NULL; while ((word_token = dtok_r(save_ptr, delim, &save_ptr, &dlmfnt, &dlmbck))) { if (dlmfnt != NULL) { printf("%s", dlmfnt); //free(dlmfnt); } printf("%s", word_token); putchar(dlmbck); } putchar('\n'); } } /* Close the decomp file */ int fclose_res = fclose(dcomp_file); assert(fclose_res == 0); /* Stop screen from disappearing until any character is pressed */ getchar(); return EXIT_SUCCESS; }
现象
- 保留
//free(dlmfnt)注释时,输出与目标文件内容完全一致。 - 取消注释
free(dlmfnt)后,输出出现乱码(如-d8、-dOld等错误内容)。
原因分析
问题根源在dtok_r函数的指针操作错误:
在给dlmfnt分配内存并赋值的代码段中,字符串终止符的赋值语句写法错误:
*(dlmfnt)[dlmfnt_len] = '\0';
根据C语言运算符优先级,[]优先级高于*,这条语句实际等价于:
*(*(dlmfnt + dlmfnt_len)) = '\0';
这会导致两个严重问题:
- 非法访问内存:
dlmfnt是指向堆内存的指针,dlmfnt + dlmfnt_len会超出指针本身的有效范围,破坏堆结构。 - 未正确添加字符串终止符:
*dlmfnt指向的内存块没有被正确终止,printf("%s", dlmfnt)会读取到内存中的随机垃圾数据。 - 不释放内存时,堆损坏暂时未影响输出;调用
free(dlmfnt)后,堆损坏暴露,覆盖后续有效数据,最终出现乱码。
解决方法
步骤1:修复dtok_r中的指针错误
将dtok.c中的错误语句:
*(dlmfnt)[dlmfnt_len] = '\0';
修改为正确的指针取值写法:
(*dlmfnt)[dlmfnt_len] = '\0';
也可以用更简洁的strndup(系统支持的情况下)替代手动分配与拷贝:
*dlmfnt = strndup(s, dlmfnt_len);
步骤2:确保内存正常释放
修复指针错误后,main.c中取消free(dlmfnt)的注释即可,此时内存释放不会再破坏堆结构,输出恢复正常,同时解决了内存泄漏问题。
内容的提问来源于stack exchange,提问作者DirtyV
相关产品推荐
相关产品推荐

