C程序内存泄漏排查求助:基于GLib哈希表的代码问题
C语言GLib哈希表内存泄漏排查与修复
核心内存问题分析
错误释放非动态分配内存
strSplit函数中free(token);完全错误:strtok返回的指针指向传入的buffer(栈上数组)内部位置,并非malloc/strdup分配的动态内存,释放它会触发非法内存访问,还可能破坏内存结构导致泄漏检测异常。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
相关产品推荐
相关产品推荐

