C语言的静态映射声明

前段时间,准备设计一个关于出错信息的表,每一个错误有一个唯一的ErrID,和对应的错误信息以及其他辅助信息。在C语言中,很自然的实现如下:
enum {
    ERR_ID_1,
    ERR_ID_2,
    ERR_ID_3,
    ERR_ID_4
} ErrID;

const char* errmsg[] = {
    
"This is Error 1 msg"/*ERR_ID_1*/
    
"This is Error 2 msg"/*ERR_ID_2*/
    
"This is Error 3 msg"/*ERR_ID_3*/
    
"This is Error 4 msg"  /*ERR_ID_4*/
};

int main() {
    printf(
"Error= %s", errmsg[ERR_ID_1]);
    
return 0;
}

这样带来的问题是很不容易维护,必须人为的确定每一个ErrID和它的errmsg相对应。希望能不增加任何时间或空间的开销的情况下,让实现更加的“美观”,更容易维护。自己第一时间能想到的,就是宏或者模板。

方法一

 

#define ERR_MSG(id, msg) const char* err_msg_##id = msg;
#define GET_MSG(id) (err_msg_##id)

ERR_MSG(ERR_ID_1, 
"This is Error 1 msg")
ERR_MSG(ERR_ID_2, 
"This is Error 2 msg")
ERR_MSG(ERR_ID_3, 
"This is Error 3 msg")
ERR_MSG(ERR_ID_4, 
"This is Error 4 msg")

int main() {
    printf(
"Error= %s", GET_MSG(ERR_ID_1));
    
return 0;
}

这个方法的好处在于连errmsg数组的空间都省略掉了。缺点就是

  1. 不能支持在运行时通过传入的ErrID来动态决定输出。
  2. 不支持基于errmsg的遍历操作甚至不知道ErrID的个数。因为errmsg本来就不存在。

方法二

首先,另外建一个文件,比如err.txt,每行的格式如:
ERR_MSG(ERR_ID_1, "This is Error 1 msg")
ERR_MSG(ERR_ID_2, "This is Error 2 msg")
ERR_MSG(ERR_ID_3, "This is Error 3 msg")
ERR_MSG(ERR_ID_4, "This is Error 4 msg")

在主文件中:

#undef ERR_MSG
#define ERR_MSG(id, msg) id,
enum {
    #include 
"err.txt"
    MAX_ERR_NUMBER
} ERRID;

#undef ERR_MSG
#define ERR_MSG(id, msg) msg,
const char* errmsg[] = {
    #include 
"err.txt"
    
""
};

在最后加入一个无用的元素,用来避免某些编译器监测到最尾元素后有逗号时的警告。
当然,errmsg数组也不一定需要。比如,可以这样实现一个通过传入的ErrID返回相应errmsg的函数get_err_msg():

#undef ERR_MSG
#define ERR_MSG(id, msg) case id: return msg; break;
const char* get_err_msg(ErrId eid) {
    
switch (eid) {
        #include 
"err.txt"
    }
    
return NULL; // Dummy。用来消除某些编译器的警告。
}
 
### C语言静态Hashmap的实现原理 #### 1. 基本概念 静态 `hashmap` 是一种基于固定大小数组的数据结构,用于快速查找键值对。其核心思想是通过哈希函数将键映射到数组的一个索引位置,并在该位置存储对应的值。如果发生冲突(即多个键被映射到同一个索引),通常采用链地址法或其他解决策略。 静态 hashmap 的特点是数组大小固定,在初始化时分配好内存空间,后续不再扩展或收缩。这种设计适合场景中已知最大可能的键数量且不需要频繁调整容量的情况[^1]。 --- #### 2. 数据结构定义 静态 hashmap 的基本组成如下: - **数组**:作为底层容器,每个元素可以是一个桶(bucket),或者直接存储键值对。 - **哈希函数**:负责将键转换为数组索引。 - **冲突处理机制**:常见的方法包括链地址法、开放寻址法等。 以下是使用链地址法的简单实现示例: ```c #include <stdio.h> #include <stdlib.h> #define TABLE_SIZE 10 // 静态数组大小 typedef struct Node { int key; int value; struct Node *next; } Node; Node* hash_table[TABLE_SIZE]; // 初始化哈希表 void init_hash_table() { for (int i = 0; i < TABLE_SIZE; ++i) { hash_table[i] = NULL; } } // 哈希函数 unsigned int hash_function(int key) { return abs(key) % TABLE_SIZE; } // 插入操作 void insert(int key, int value) { unsigned int index = hash_function(key); Node* new_node = (Node*)malloc(sizeof(Node)); new_node->key = key; new_node->value = value; new_node->next = hash_table[index]; hash_table[index] = new_node; } // 查找操作 int search(int key) { unsigned int index = hash_function(key); Node* current = hash_table[index]; while (current != NULL) { if (current->key == key) { return current->value; } current = current->next; } return -1; // 表示未找到 } // 删除操作 void delete_key(int key) { unsigned int index = hash_function(key); Node* current = hash_table[index]; Node* prev = NULL; while (current != NULL && current->key != key) { prev = current; current = current->next; } if (current == NULL) return; // 键不存在 if (prev == NULL) { hash_table[index] = current->next; } else { prev->next = current->next; } free(current); } ``` --- #### 3. 关键点分析 ##### 3.1 数组大小的选择 静态 hashmap 的性能高度依赖于数组大小和负载因子(实际存储的键值对数目 / 数组长度)。过小的数组可能导致大量冲突,降低效率;过大则浪费内存资源。因此需根据具体需求合理设置 `TABLE_SIZE`。 ##### 3.2 哈希函数的设计 一个好的哈希函数应满足均匀分布性和高效性两个条件。上述例子中的 `abs(key) % TABLE_SIZE` 是最简单的形式之一,但在某些情况下可能会导致较差的分布效果。更复杂的算法如 FNV 或 MurmurHash 可提供更好的性能[^2]。 ##### 3.3 冲突解决策略 链地址法是最常用的解决方案之一,因为它易于实现并能有效应对高碰撞率的情形。然而对于极端情况下的长链表访问开销较大,此时可考虑其他替代方案比如二次探测法或双散列技术[^3]。 --- #### 4. 总结 C语言静态 hashmap 的实现涉及基础理论知识与工程实践技巧相结合的过程。理解如何选取合适的参数配置以及优化各个组成部分将是掌握这一工具的关键所在。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值