C语言接口与实现: Atom

来源:互联网 发布:聚合数据接口怎么用呀 编辑:程序博客网 时间:2024/05/16 17:27

原子(Atom)这一数据结构的工作原理基本类似于拉链式哈希表,每个原子对应唯一的字符串,不同的原子对应的字符串内容不同(用数学语言讲就是在原子和字符串之间建立了双射)。原子的特点有三个:

每个原子对应的字符串是不可变的

其二,相同内容的字符串只会保存一次,节省了存储空间;

其三,比较两个字符串是否相同时不必知道字符串的内容,而只需比较它们对应的指针。

而原子为什么有这三个特点,之后在代码中会详细解释。


首先看atom的定义:

static struct atom {    char *str;    int len;    struct atom *link;} *buckets[2048];

因为使用者并不需要知道原子的定义,所以这部分是放在实现文件里的,但是为了便于理解提到了前面。每个原子对应唯一的字符串——str指向这个字符串,len记录这个字符串的长度,link是为了实现链表,然后做了一个数组buckets[2048],则是为了实现hash(相当于有2048个桶,每个桶存放一个原子链表)。当要查找一个特定字符串是否已经保存时,就是先用hash算法找到它对应的桶,然后顺着这个桶为首 的原子链表查找。


然后看头文件定义:

// *** atom.h ***#ifndef ATOM_INCLUDED#define ATOM_INCLUDEDextern const char *Atom_new(const char *str, int len);extern const char *Atom_string(const char *str);extern const char *Atom_int(long n);extern int Atom_length(const char *str);#endif
特别注意因为原子对应的字符串是不可变的,所以返回的是常指针。

1)Atom_new 的功能是确定与str指向的长度为len的字符串内容相同的字符串已经有原子对应,如果有就找到这个原子返回字符串指针,没有则新建原子返回字符串指针;

2)Atom_string的功能是查找str是否有对应原子,有则找到原子并返回其指针,否则新建原子并返回指针;

3)Atom_int的功能是在做Atom_string之前多一步把长整数转成字符串的工作;

4)Atom_length的功能是如果str有对应原子,就返回长度,否则报错。

所以四个函数的关系是:

 Atom_new是核心,Atom_string和Atom_int调用Atom_new,即在Atom_new基础上提供了一层封装,Atom_length相当于副产物。


接着看实现文件:

// *** atom.c ***#include <string.h>#include <assert.h>#include <limits.h>#include "atom.h"// 计算数组的长度#define NELEMS(x) ((sizeof (x))/(sizeof ((x)[0])))// 原子的定义static struct atom {char *str;int len;struct atom *link;} *buckets[2048];// 随机数数组,用于实现hashstatic unsigned long scatter[256] = {22547, 26788, 15942, 25232, 5265, 13989, 10770, 32000,3589, 18744, 27081, 31436, 29712, 13837, 3754, 28998,27528, 8239, 15077, 6126, 13248, 22583, 4353, 2628,12553, 19648, 12923, 23514, 120, 24346, 4528, 12973,30954, 28739, 6908, 23477, 15686, 14866, 31012, 3515,17410, 18614, 30961, 20075, 30697, 10335, 20510, 11690,22043, 6971, 19317, 19242, 11605, 27556, 10292, 23099,25202, 11928, 7349, 6116, 24114, 2665, 21582, 26418,2107, 21941, 25207, 2000, 29533, 8857, 28970, 6872,7962, 2594, 11920, 27113, 29286, 19460, 15833, 9338,21419, 5775, 12139, 1901, 528, 2782, 17472, 8935,5115, 20818, 137, 12896, 16084, 21055, 20888, 15094,19469, 26340, 19196, 25607, 24415, 5793, 16110, 15870,29270, 13412, 13804, 27501, 10511, 28773, 2467, 24669,25665, 12819, 10002, 11234, 13434, 20045, 15341, 32754,4779, 28118, 21311, 27510, 487, 14339, 9556, 31394,32360, 5559, 7894, 12674, 24019, 8337, 25398, 5334,1212, 4708, 20045, 13600, 5485, 18664, 14660, 28667,31715, 12719, 1713, 23470, 31514, 12890, 14850, 4353,8075, 24844, 7711, 18256, 15090, 8479, 16203, 30429,19796, 30742, 15435, 30058, 32577, 11987, 13003, 16648,4439, 4875, 19197, 27946, 25863, 27894, 9859, 26109,668, 32075, 13967, 32252, 16504, 5995, 15689, 31167,28577, 11228, 32369, 3603, 12669, 22266, 25413, 1104,23258, 18409, 5541, 30951, 2097, 5070, 22133, 26683,18220, 18751, 4931, 32178, 31077, 22520, 32553, 22795,21617, 8399, 13045, 15858, 8989, 16874, 6280, 25041,11255, 29487, 9596, 19434, 24849, 1788, 10506, 31651,15617, 25368, 12586, 18720, 28972, 1514, 19195, 13335,18757, 27625, 8806, 5344, 26640, 26964, 17662, 25548,16748, 19491, 3115, 2744, 30096, 23101, 29320, 29922,29077, 32747, 22665, 20731, 31029, 17037, 31841, 14717};const char *Atom_new(const char *str, int len) {unsigned long h;int i;struct atom *p;// 验证输入str和len是否有效assert(str);assert(len >= 0);// 计算hash到的桶编号for (h = 0, i = 0; i < len; i++)h = (h << 1) + scatter[(unsigned char)str[i]];h &= NELEMS(buckets) - 1;// 沿链表查找是否有原子保存了内容相同的字符串,有则直接返回指针for (p = buckets[h]; p; p = p->link) {if (len == p->len) {for (i = 0; i < len && p->str[i] == str[i];)++i;if (i == len)return p->str;}}// 新建原子,保存字符串p = (struct atom*)malloc(sizeof(struct atom));p->len = len;p->str = (char*)malloc(sizeof(char) * (len + 1));if (len > 0)memcpy(p->str, str, len);p->str[len] = '\0';// 把新原子插入所在桶对应链表的头部,并返回指针p->link = buckets[h];buckets[h] = p;return p->str;}const char *Atom_string(const char *str) {// 验证str有效后调用Atom_newassert(str);return Atom_new(str, strlen(str));}const char *Atom_int(long n) {// vs2013中LONG_MIN = -2147483648,转成字符串长度至多为11char str[12];// 把s指向str中最后一个字符char *s = str + sizeof(str);// 把n转换成字符串unsigned long m;if (n == LONG_MIN)m = LONG_MAX + 1UL;else if (n < 0)m = -n;elsem = n;while (m > 0) {*(--s) = m % 10 + '0';m /= 10;}if (n < 0)*(--s) = '-';// 调用Atom_newreturn Atom_new(s, (str + sizeof str) - s);}int Atom_length(const char *str) {struct atom *p;int i;assert(str);// 遍历所有桶中的各个链表,找到对应原子则返回长度for (i = 0; i < NELEMS(buckets); ++i)for (p = buckets[i]; p; p = p->link)if (p->str == str)return p->len;// 没有对应原子,返回-1return -1;}


最后进行测试:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include "Atom.h"int main(){char s1[] = "hello, world";char s2[] = "nice day";long n1 = 837;long n2 = LONG_MIN;char* pc = NULL;printf("round 1\n");printf("s1: %d\n", Atom_string(s1));printf("s2: %d\n", Atom_string(s2));printf("n1: %d\n", Atom_int(n1));printf("n2: %d\n", Atom_int(n2));printf("\nround 2\n");printf("s1: %d\n", Atom_string(s1));printf("s2: %d\n", Atom_string(s2));printf("n1: %d\n", Atom_int(n1));printf("n2: %d\n", Atom_int(n2));printf("\n");printf("s1长度 = %d\n", Atom_length(Atom_string(s1)));printf("s2长度 = %d\n", Atom_length(Atom_string(s2)));printf("n1长度 = %d\n", Atom_length(Atom_int(n1)));printf("n2长度 = %d\n", Atom_length(Atom_int(n2)));printf("非原子长度 = %d\n\n", Atom_length(Atom_length("vooon")));system("pause");return 0;}
0 0
原创粉丝点击