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

C语言调用json-c的json_object_get_string出现内存泄漏问题

问题根因

首先纠正认知偏差:json_object_get_string接口本身不会产生需要用户手动释放的内存,它返回的是json-c库内部维护的字符串指针,生命周期和所属的json_object实例绑定,一旦调用json_object_put将对象引用计数减到0,这个指针就会成为野指针,你用strdup做字符串拷贝的操作本身是正确的。
你遇到的内存泄漏是代码本身的逻辑缺陷导致的,和json-c接口无关,核心问题有三个:

  • 分支资源泄漏:当json文件解析失败、或者字段校验不匹配时,你没有调用json_object_put释放json_object_from_file返回的根对象,每次循环走到异常分支都会泄漏一整个json解析后的对象树。
  • 缺失自定义内存释放逻辑:你通过strdup为结构体成员分配的堆内存,在结构体生命周期结束后没有配套的释放逻辑,valgrind报的泄漏绝大多数来自这部分未释放的自分配内存。
  • 逻辑笔误:原代码中examples[i] = container属于未定义变量错误,且当循环中出现字段校验不通过的场景时,循环变量i和实际有效元素计数count不匹配,直接用i做数组索引会出现内存越界。

你之前尝试存入字符串后立刻free指针导致值丢失的原因很简单:strdup返回的是堆内存的地址,你把地址存在结构体里,立刻free相当于把这块内存的所有权归还给操作系统,结构体里存的地址变成野指针,自然读不到有效内容——你存的是内存地址,不是字符串的拷贝,释放地址指向的内存后内容就不存在了。

修复方案

首先修正list函数的逻辑漏洞,同时补充配套的资源释放函数,所有从list拿到的结构体实例,用完后必须调用释放函数回收内存。
修正后的完整代码如下:

struct Example {
    char *id;
    char *name;
    char *logPath;
};

struct Examples {
    struct Example *examples;
    size_t size;
};

// 提前声明释放函数,供异常分支回滚调用
void free_examples_list(struct Examples list);

struct Examples list() {
    size_t i;
    size_t count = 0;
    struct Example *examples = NULL;
    struct Examples examplesList = {.examples = NULL, .size = 0};

    for (i = 0; i < 59; i++) {
        json_object *root_obj = json_object_from_file("path.json");
        // 解析失败直接跳过,避免空指针访问
        if (root_obj == NULL) {
            continue;
        }
        json_object *jID;
        json_object *jName;
        json_object *jLogPath;

        // 字段校验不通过时先释放根对象,再进入下一轮循环
        if (json_object_object_get_ex(root_obj, "Name", &jName) != TRUE ||
            json_object_object_get_ex(root_obj, "LogPath", &jLogPath) != TRUE ||
            json_object_object_get_ex(root_obj, "ID", &jID) != TRUE) {
            json_object_put(root_obj);
            continue;
        }

        char *id = strdup(json_object_get_string(jID));
        char *name = strdup(json_object_get_string(jName));
        char *logPath = strdup(json_object_get_string(jLogPath));
        // 拿到字符串拷贝后立刻释放json对象,缩短对象生命周期
        json_object_put(root_obj);

        // 处理strdup分配失败的异常场景,回滚已分配资源
        if (id == NULL || name == NULL || logPath == NULL) {
            free(id);
            free(name);
            free(logPath);
            free_examples_list(examplesList);
            die("Strdup alloc failed");
        }

        count++;
        struct Example *tmpExamples = realloc(examples, count * sizeof(struct Example));
        if (tmpExamples == NULL) {
            free(id);
            free(name);
            free(logPath);
            free_examples_list(examplesList);
            die("Realloc failed");
        }
        examples = tmpExamples;
        // 用实际有效元素计数做索引,避免越界
        examples[count - 1] = (struct Example){
            .id = id,
            .name = name,
            .logPath = logPath
        };
        examplesList.examples = examples;
        examplesList.size = count;
    }

    return examplesList;
}

// 配套的资源释放函数
void free_examples_list(struct Examples list) {
    if (list.examples == NULL) {
        return;
    }
    for (size_t idx = 0; idx < list.size; idx++) {
        // 逐个释放strdup分配的字符串
        free(list.examples[idx].id);
        free(list.examples[idx].name);
        free(list.examples[idx].logPath);
    }
    // 释放示例数组本身
    free(list.examples);
}
注意事项
  • json-c库的内存管理遵循引用计数规则:所有通过json_object_from_file、json_object_new_*接口创建的对象,最终必须配对调用json_object_put,不要手动释放json_object_get_string返回的内部指针,库会在对象销毁时自动回收这部分内存。
  • C语言中所有通过strdup、malloc、calloc、realloc分配的堆内存,必须在确定不再使用后调用free回收,跨函数传递内存时要明确所有权归属,避免出现只分配不释放的泄漏问题。

内容的提问来源于stack exchange,提问作者jcarlosweb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:27:22