You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

打印字符串后释放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';

这会导致两个严重问题:

  1. 非法访问内存:dlmfnt是指向堆内存的指针,dlmfnt + dlmfnt_len会超出指针本身的有效范围,破坏堆结构。
  2. 未正确添加字符串终止符:*dlmfnt指向的内存块没有被正确终止,printf("%s", dlmfnt)会读取到内存中的随机垃圾数据。
  3. 不释放内存时,堆损坏暂时未影响输出;调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:25:37