GHashTable replace异常:替换所有键值而非指定单个键值
问题:GHashTable replace操作意外修改所有键对应的值
我正在编写一个读取.csv文件并将信息存储、排序到GHashTable中以供后续访问的函数。我希望使用glib哈希表的replace函数,当指定键已有值时仅更新该键对应的值,但实际操作中却修改了所有键的内容。请问我是否使用了错误的函数,或是需要换一种实现思路?
#include <glib.h> #include <stdio.h> #include <stdlib.h> #include "../includes/estatisticas.h" #include "../includes/Drivers.h" #define MAX_LENGHT_RIDES 2000000 GHashTable** rides(GHashTable** arr_drivers){ FILE* rides_files = fopen("../Entrada/rides.csv", "r"); GHashTable* h_preco_medio = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); GHashTable* h_number_city = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); char* ptr; char* city = malloc(MAX_LENGHT_RIDES); char* str = calloc(1, MAX_LENGHT_RIDES); fgets(str, MAX_LENGHT_RIDES, rides_files); while (fgets(str, MAX_LENGHT_RIDES, rides_files)){ char* line = str; char* id = strdup(strsep(&line, ";\n")); char* date = strdup(strsep(&line, ";\n")); char* driver = strdup(strsep(&line, ";\n")); char* user = strdup(strsep(&line, ";\n")); city = strdup(strsep(&line, ";\n")); double distance = strtod(strdup(strsep(&line, ";\n")), &ptr); char* score_user = strdup(strsep(&line, ";\n")); char* score_driver = strdup(strsep(&line, ";\n")); double tip = strtod(strdup(strsep(&line, ";\n ")), &ptr); char* comment = strdup(strsep(&line, ";\n ")); char* car_class = (char*) g_hash_table_lookup(arr_drivers[0], driver); double* preco; preco[0] = calc_preco(distance, car_class); if(!g_hash_table_lookup(h_preco_medio, city)){ g_hash_table_insert(h_preco_medio, city, preco); int* city_counter; city_counter[0] = 1; g_hash_table_insert(h_number_city, city, city_counter); } else{ double* novo_preco; novo_preco[0] = ((double*) g_hash_table_lookup(h_preco_medio, city))[0] + preco[0]; g_hash_table_replace(h_preco_medio, city, novo_preco); int* new_city_counter; new_city_counter[0] = ((int*) g_hash_table_lookup(h_number_city, city))[0] + 1; g_hash_table_replace(h_number_city, city, new_city_counter); printf("%d\n", ((int*) g_hash_table_lookup(h_number_city, "Faro"))[0]); } } GHashTable** arr = calloc(5, sizeof(GHashTable*)); arr[0] = h_preco_medio; arr[1] = h_number_city; return arr; }
错误分析
问题和g_hash_table_replace函数本身无关,核心原因是未初始化指针直接赋值导致的内存越界与野指针行为:
- 野指针写入:比如
double* preco; preco[0] = calc_preco(...);,preco是未初始化的指针,指向随机内存地址。写入该地址会覆盖其他内存区域的数据,而这些区域可能被哈希表中其他键的数值指针共享,最终导致所有键的内容被意外修改。 - 重复的野指针问题:
int* city_counter; city_counter[0] = 1;、double* novo_preco; novo_preco[0] = ...都是同样的错误,未分配内存就直接解引用指针,破坏了内存结构。 - 内存泄漏:初始
char* city = malloc(MAX_LENGHT_RIDES);分配的内存被后续city = strdup(...)的指针覆盖,无法释放;同时id、date等临时变量未释放,造成大量内存泄漏。
修复方案
核心解决思路是为每个数值指针分配独立内存,同时修正内存管理逻辑:
#include <glib.h> #include <stdio.h> #include <stdlib.h> #include "../includes/estatisticas.h" #include "../includes/Drivers.h" #define MAX_LENGHT_RIDES 2000000 GHashTable** rides(GHashTable** arr_drivers){ FILE* rides_files = fopen("../Entrada/rides.csv", "r"); if (!rides_files) { perror("Failed to open rides.csv"); return NULL; } // 指定键和值的销毁函数,避免内存泄漏 GHashTable* h_preco_medio = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); GHashTable* h_number_city = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); char* str = calloc(1, MAX_LENGHT_RIDES); if (!str) { fclose(rides_files); return NULL; } // 跳过表头 fgets(str, MAX_LENGHT_RIDES, rides_files); while (fgets(str, MAX_LENGHT_RIDES, rides_files)){ char* line = str; // 解析字段 char* id = strdup(strsep(&line, ";\n")); char* date = strdup(strsep(&line, ";\n")); char* driver = strdup(strsep(&line, ";\n")); char* user = strdup(strsep(&line, ";\n")); char* city = strdup(strsep(&line, ";\n")); double distance = strtod(strsep(&line, ";\n"), NULL); char* score_user = strdup(strsep(&line, ";\n")); char* score_driver = strdup(strsep(&line, ";\n")); double tip = strtod(strsep(&line, ";\n\r"), NULL); char* comment = strdup(strsep(&line, ";\n\r")); char* car_class = (char*) g_hash_table_lookup(arr_drivers[0], driver); double price = calc_preco(distance, car_class); // 查找城市对应的现有数据 double* existing_price = g_hash_table_lookup(h_preco_medio, city); int* existing_count = g_hash_table_lookup(h_number_city, city); if (!existing_price) { // 首次插入,分配独立内存存储数值 double* new_price = g_malloc(sizeof(double)); *new_price = price; g_hash_table_insert(h_preco_medio, city, new_price); int* new_count = g_malloc(sizeof(int)); *new_count = 1; g_hash_table_insert(h_number_city, city, new_count); } else { // 直接修改哈希表中已有的内存,无需调用replace *existing_price += price; *existing_count += 1; printf("%d\n", *existing_count); } // 释放临时解析的字段 g_free(id); g_free(date); g_free(driver); g_free(user); g_free(score_user); g_free(score_driver); g_free(comment); } GHashTable** arr = g_malloc0(5 * sizeof(GHashTable*)); arr[0] = h_preco_medio; arr[1] = h_number_city; g_free(str); fclose(rides_files); return arr; }
关键修改点:
- 为存储数值的指针(
double*、int*)分配独立内存,避免野指针写入。 - 更新数值时直接修改哈希表中已有的内存,无需调用
g_hash_table_replace——键未变化时,直接修改值更高效。 - 为哈希表指定销毁函数,确保哈希表销毁时自动释放键和值的内存。
- 释放所有临时解析字段,修复内存泄漏问题。
- 添加文件打开、内存分配的错误检查,增强代码健壮性。
内容的提问来源于stack exchange,提问作者TuBzz
相关产品推荐
相关产品推荐

