使用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
相关产品推荐
相关产品推荐

