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

C程序内存泄漏排查求助:基于GLib哈希表的代码问题

C语言GLib哈希表内存泄漏排查与修复

核心内存问题分析

  • 错误释放非动态分配内存

    1. strSplit函数中free(token);完全错误:strtok返回的指针指向传入的buffer(栈上数组)内部位置,并非malloc/strdup分配的动态内存,释放它会触发非法内存访问,还可能破坏内存结构导致泄漏检测异常。
    2. main函数中free(splitedStr);错误:splitedStr指向全局数组tokArr,全局内存并非动态分配,free全局指针属于未定义行为,会干扰valgrind的检测结果。
  • 哈希表内存管理冲突
    在printHash中直接free(key)和free(val)后,g_hash_table_destroy(hash)虽不会重复释放(创建哈希表时未指定键值销毁函数),但这种操作不规范,若遍历过程出现异常,可能导致部分内存未被正确释放。更合理的方式是使用g_hash_table_new_full指定键值销毁函数,让哈希表自行管理内存。

  • 全局数组的不合理使用
    tokArr作为全局数组,每次调用strSplit都会覆盖之前内容,虽当前代码用strdup复制内容未直接导致泄漏,但全局变量会降低代码可维护性,易引发意外内存问题。

修复后的代码

#include <stdio.h>
#include <glib.h>
#include <string.h>
#define MAX_LENGTH 256

// 改用static局部数组,避免全局变量问题
char** strSplit(char *oneStr, const char* delim, int *tokenCount)
{
    static char *tokArr[MAX_LENGTH];
    int index = 0;
    char *token = strtok(oneStr, delim);

    while (token != NULL && index < MAX_LENGTH - 1) {
        tokArr[index++] = token;
        token = strtok(NULL, delim);
    }
    tokArr[index] = NULL; // 添加结束标记,避免越界访问
    if (tokenCount != NULL) {
        *tokenCount = index;
    }
    return tokArr;
}

void printHash(gpointer key, gpointer val, gpointer userData)
{
    printf(userData, (char*)key, (char*)val);
    // 不再手动释放,交给哈希表销毁函数处理
}

int main(int argc, char const *argv[])
{   
    if (argc != 2) {
        printf("need a file name as an argument\n");
        return -1;
    }

    const char *filePath = argv[1];
    FILE *fp = fopen(filePath, "r");

    if (fp == NULL) {
        printf("error: cannot open document %s\n", filePath);
        return -1;
    }

    // 使用g_hash_table_new_full,指定键值销毁函数为g_free
    GHashTable* hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    char buffer[MAX_LENGTH];
    while (fgets(buffer, MAX_LENGTH, fp)) {
        // 去除换行符,避免值中包含不必要的换行
        buffer[strcspn(buffer, "\n")] = '\0';
        
        int tokenCount = 0;
        char **splitedStr = strSplit(buffer, "@@@@", &tokenCount);
        // 确保分割出至少两个部分,避免数组越界
        if (tokenCount >= 2) {
            char *key = strdup(splitedStr[0]);
            char *val = strdup(splitedStr[1]);
            g_hash_table_insert(hash, key, val);
        }
    }
    fclose(fp);
    g_hash_table_foreach(hash, (GHFunc)printHash, "Name: %s, age: %s\n");
    // 销毁哈希表时自动调用g_free释放所有键值
    g_hash_table_destroy(hash);
    return 0;
}

额外优化说明

  • 给strSplit添加tokenCount参数,返回分割得到的token数量,避免访问越界。
  • 使用strcspn去除fgets读取到的换行符,避免存储的值包含不必要的换行。
  • 改用g_hash_table_new_full并传入g_free作为键值销毁函数,让GLib自动管理哈希表中键值的内存释放,简化代码并避免手动释放的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:46:22