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

使用hashmap.c存储SpriteSheet后访问出现段错误的原因与解决

问题

我尝试用hashmap.c实现哈希表存储SpriteSheet,从JSON文件读取数据,用cJSON解析。

SpriteSheet结构体

typedef struct
{
    char* name;
    char* path;
    int width;
    int height;
} SpriteSheet;

示例JSON数据

"spriteSheets": [
    {
        "name": "player",
        "path": "player.png",
        "width": 192,
        "height": 64
    }
]

填充哈希表的代码

spriteSheetHashMap = hashmap_new(sizeof(SpriteSheet), 0, 0, 0, spriteSheetHash, spriteSheetCompare, NULL, NULL);

cJSON* textureJson = cJSON_Parse(textureJsonString);

cJSON* spriteSheetsJson = cJSON_GetObjectItemCaseSensitive(textureJson, "spriteSheets");

cJSON* spriteSheetJson;
cJSON_ArrayForEach(spriteSheetJson, spriteSheetsJson)
{
    char* sheetName = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "name")->valuestring;
    char* sheetPath = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "path")->valuestring;
    int sheetWidth = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "width")->valueint;
    int sheetHeight = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "height")->valueint;

    hashmap_set(spriteSheetHashMap, &(SpriteSheet){
        .name=sheetName,
        .path=sheetPath,
        .width=sheetWidth, .height=sheetHeight,
        .spriteHashMap=spriteHashMap
    });
}

cJSON_Delete(textureJson);

错误现象

在其他文件访问哈希表数据时触发段错误:

SpriteSheet* spriteSheet = hashmap_get(spriteSheetHashMap, &(SpriteSheet){ .name="map" });
printf("%s, %s", spriteSheet->name, spriteSheet->path); // 此行触发段错误

正常情况

手动添加项则能正常运行:

hashmap_set(spriteSheetHashMap, &(SpriteSheet){
        .name="map",
        .path="image.png",
        .width=4, .height=8
});
// 或者
char* a = "map";
char* b = "image.png";
hashmap_set(spriteSheetHashMap, &(SpriteSheet){
        .name=a,
        .path=b,
        .width=4, .height=8
});

原因分析

问题出在cJSON的内存管理逻辑:

  • cJSON解析返回的valuestring指向的是cJSON内部分配的内存,这块内存会在调用cJSON_Delete(textureJson)时被整体释放。
  • 填充哈希表时直接将sheetName和sheetPath(即cJSON内部的字符串指针)存入结构体,cJSON释放内存后,这些指针就变成了野指针,后续访问必然触发段错误。
  • 手动添加时使用的是字符串字面量,这类数据存储在程序的只读数据段,生命周期伴随整个程序,不会被释放,因此访问正常。

修复方案

需要为SpriteSheet的name和path单独分配内存,复制cJSON返回的字符串内容,而非直接复用指针:

修改填充哈希表的代码

cJSON_ArrayForEach(spriteSheetJson, spriteSheetsJson)
{
    cJSON* nameItem = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "name");
    cJSON* pathItem = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "path");
    int sheetWidth = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "width")->valueint;
    int sheetHeight = cJSON_GetObjectItemCaseSensitive(spriteSheetJson, "height")->valueint;

    // 为字符串分配内存并复制内容
    char* sheetName = malloc(strlen(nameItem->valuestring) + 1);
    char* sheetPath = malloc(strlen(pathItem->valuestring) + 1);
    strcpy(sheetName, nameItem->valuestring);
    strcpy(sheetPath, pathItem->valuestring);

    hashmap_set(spriteSheetHashMap, &(SpriteSheet){
        .name=sheetName,
        .path=sheetPath,
        .width=sheetWidth,
        .height=sheetHeight,
        .spriteHashMap=spriteHashMap
    });
}

cJSON_Delete(textureJson);

处理内存泄漏

哈希表销毁时,需要遍历所有SpriteSheet项,释放name和path的内存。可以在创建哈希表时指定自定义释放回调:

void spriteSheetFree(void* item)
{
    SpriteSheet* sheet = (SpriteSheet*)item;
    free(sheet->name);
    free(sheet->path);
}

// 创建哈希表时传入释放回调
spriteSheetHashMap = hashmap_new(sizeof(SpriteSheet), 0, 0, 0, spriteSheetHash, spriteSheetCompare, spriteSheetFree, NULL);

内容的提问来源于stack exchange,提问作者Techie-Guy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:25:43