C语言实现字符串为键、函数为值的字典,用于Allegro游戏开发
实现方案建议
1. 基础哈希表结构设计
针对你需要的「字符串为键、函数指针为值」的映射需求,可采用链地址法解决哈希冲突的哈希表实现,结构定义参考如下:
// 可根据你的游戏回调签名调整通用函数指针定义 typedef void (*GameAction)(void* args); // 哈希节点:存储单组键值对 typedef struct HashNode { char* key; // 字符串键,可存动作名、皮肤ID等 GameAction value; // 对应执行的函数指针 struct HashNode* next; // 链表处理哈希冲突 } HashNode; // 哈希表主体 typedef struct HashTable { int bucket_count; // 总桶数,建议取大于预计键数量1.5倍的质数 HashNode** buckets; // 桶数组 } HashTable;
该实现逻辑简单,对游戏场景的性能需求完全足够,你熟悉动态内存管理的话可以快速完成开发。
2. 核心哈希函数实现
游戏场景不需要加密级哈希,推荐用性能高、短字符串碰撞率低的DJB2哈希:
unsigned int hash(const char* key, int bucket_count) { unsigned long hash_val = 5381; int c; while ((c = *key++)) hash_val = ((hash_val << 5) + hash_val) + c; // 等价于 hash_val *33 +c return hash_val % bucket_count; }
3. 核心操作接口实现
哈希表初始化
HashTable* hash_table_create(int bucket_count) { HashTable* table = malloc(sizeof(HashTable)); table->bucket_count = bucket_count; table->buckets = calloc(bucket_count, sizeof(HashNode*)); return table; }
键值对插入/覆盖
void hash_table_put(HashTable* table, const char* key, GameAction action) { unsigned int idx = hash(key, table->bucket_count); // 先查找已存在的键,存在则直接覆盖值(适配皮肤切换逻辑) HashNode* cur = table->buckets[idx]; while (cur) { if (strcmp(cur->key, key) == 0) { cur->value = action; return; } cur = cur->next; } // 不存在则新建节点头插 HashNode* new_node = malloc(sizeof(HashNode)); new_node->key = strdup(key); new_node->value = action; new_node->next = table->buckets[idx]; table->buckets[idx] = new_node; }
这里的覆盖逻辑正好适配你的皮肤需求:同一个动作键比如"player_jump",换皮肤时直接插入新的跳跃动画函数即可自动覆盖旧逻辑,不需要修改任何调用代码。
函数查询调用
void hash_table_invoke(HashTable* table, const char* key, void* args) { unsigned int idx = hash(key, table->bucket_count); HashNode* cur = table->buckets[idx]; while (cur) { if (strcmp(cur->key, key) == 0) { cur->value(args); // 直接执行对应函数 return; } cur = cur->next; } // 可选添加找不到键的默认处理,比如日志打印、执行兜底动作 }
4. 游戏场景使用示例
原来的多层if判断逻辑:
if (strcmp(action_name, "jump") == 0) { default_jump(args); } else if (strcmp(action_name, "attack") == 0) { default_attack(args); } // ... 更多动作判断
改造后:
// 初始化阶段注册所有默认动作 hash_table_put(action_map, "jump", default_jump); hash_table_put(action_map, "attack", default_attack); hash_table_put(action_map, "walk", default_walk); // 执行动作时一行代码搞定,无任何if判断 hash_table_invoke(action_map, action_name, args); // 切换皮肤时直接覆盖对应动作即可 hash_table_put(action_map, "jump", skin2_jump); hash_table_put(action_map, "attack", skin2_attack);
调用逻辑完全不需要调整,适配性极强。
5. 优化建议
- 如果你的动作总数少于30个,甚至可以不用哈希表,直接做
{键, 函数指针}的结构体数组,遍历查询即可,实现更简单,性能也完全满足小游戏需求 - 函数指针的签名可以根据实际需求调整,比如添加返回值、改为Allegro事件/位图参数都可以,只需要修改
GameAction的定义 - 可增加懒加载逻辑,皮肤对应的动作函数仅在第一次使用时注册,降低启动内存占用
内容的提问来源于stack exchange,提问作者Gabriel Lüders
相关产品推荐
相关产品推荐

