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

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函数本身无关,核心原因是未初始化指针直接赋值导致的内存越界与野指针行为:

  1. 野指针写入:比如double* preco; preco[0] = calc_preco(...);,preco是未初始化的指针,指向随机内存地址。写入该地址会覆盖其他内存区域的数据,而这些区域可能被哈希表中其他键的数值指针共享,最终导致所有键的内容被意外修改。
  2. 重复的野指针问题:int* city_counter; city_counter[0] = 1;、double* novo_preco; novo_preco[0] = ...都是同样的错误,未分配内存就直接解引用指针,破坏了内存结构。
  3. 内存泄漏:初始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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:55:22