缘由
经常读到这样那样的技术,hash表的技术很早就学习过了,但是一直不太明白它能拿来做什么,这次发现其可以用来完成C语言中define关键字的功能,顿时觉得非常伟大。故记录了下来。
其实现在学的什么多东西都这样的感觉,感觉学到了,了解了,但是却不知道能拿来做什么。最近了解到用C写的redis可以作为缓存来使用,以nosql方式完成,或者说是键值对的方式,速度非常快,并且一直存放在内存中。据说用了3万行左右的代码,我其实很想试着了解或者学习一下源码,这样比一直看书好多了,不那么苦闷。当然这个redis肯定也是用hash来完成的,所以,和这个主题多少还有点关系吧。说到这里unix高级环境编程的倒数第二章就是讲数据库编程的,我先看了看原理,也非常充实。不过最近阅读的C++ primer就没那么爽了,十分干燥,十分重理论,呼,稍稍抱怨几句。
其实这篇的博客的内容也就是K&R C程序设计语言 P125页的内容。我只是作了稍稍深刻的理解和敲了敲书上的例子代码。
define
大家都应该知道define关键字的作用,就是用一个字符代替另一个字符,在实际的编程中我们都是用一个字符串来代替数字,字符串能够表明这个数字的含义,编译器会在遇到这个字符串的地方通通换成,我们想要替换的数字(字也可以)。
比如这个就是大家经常遇见的:
#define MAXBUFFSIZE 1024
如果只看到1024会很迷茫,但是看到MAXBUFFSIZE就比较明白。
思路
我们用hash的散列查找方法,将输入的名字MAXBUFFSIZE,转为一个非负的整数,将这个整数作为下标,存在一个数组内。数组里存放的是一个链表的表头,每个链表元素里面存放的就是名字和被替换的文本1024,还有就是该链表的下一个元素。这样我们可以看出,我们解决hash冲突的方式就是拉链法。下图应该可以清楚阐述这一段文字:
源代码以及测试结果
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
//链表的每一个元素的结构
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
//计算hash值
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
//查找这个name
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* found */
return NULL; /* not found */
}
//插入一个新的define
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];//这里是想将新建的np插入到表头
hashtab[hashval] = np;
} else {/* already there */
free((void *) np->defn); /*free previous defn */
}
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
int main(){
struct nlist *np;
install("A","1");
install("B","2");
install("A","11");
install("ADLASDKLASJDLKASJDKLAJSDLKAJSDALSJDLKASJDLKJASLKDJASLKDJLASKJDLASJDLASJDLSAJLDAJSLKDAJSLKDJALKSJDALSD","4");
install("Z","aspodkpaoskdpaoskdpaoskdpoaskdpoakspodkaspodkapsodkaposkdpoaskdpoaskdpakspodkaspodkpaoskdpoaskdasd");
np=lookup("A");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("B");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("A");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("ADLASDKLASJDLKASJDKLAJSDLKAJSDALSJDLKASJDLKJASLKDJASLKDJLASKJDLASJDLASJDLSAJLDAJSLKDAJSLKDJALKSJDALSD");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("Z");
printf("name:%s define:%s \n",np->name,np->defn);
getchar();
return 0;
}
课后练习题
Exercise 6-5
Write a function undef that will remove a name and definition from the table maintained by lookup and install.答:
全部源代码,包括测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* found */
return NULL; /* not found */
}
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];//这里是想将新建的np插入到表头
hashtab[hashval] = np;
} else {/* already there */
free((void *) np->defn); /*free previous defn */
}
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
//删除一个已经define了的条目
int undef(char *name)
{
struct nlist *np,*pre=NULL;
unsigned h=hash(name);
for (np = hashtab[h]; np != NULL; np = np->next){
if (strcmp(name, np->name) == 0){
break;
}
pre=np;//记录np的前一个元素
}
if(np==NULL){
return -1;//本来就没有这个元素
}
if (pre==NULL)//说明np是第一个元素
{
hashtab[h]=np->next;
}else{
pre->next=np->next;
free((void *)np->name);//答案书上写的要怎么做
free((void *)np->defn);
free((void *)np);
}
return 1;
}
int main(){
struct nlist *np;
install("A","1");
install("B","2");
install("A","11");
install("ADLASDKLASJDLKASJDKLAJSDLKAJSDALSJDLKASJDLKJASLKDJASLKDJLASKJDLASJDLASJDLSAJLDAJSLKDAJSLKDJALKSJDALSD","4");
install("Z","aspodkpaoskdpaoskdpaoskdpoaskdpoakspodkaspodkapsodkaposkdpoaskdpoaskdpakspodkaspodkpaoskdpoaskdasd");
np=lookup("A");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("B");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("A");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("ADLASDKLASJDLKASJDKLAJSDLKAJSDALSJDLKASJDLKJASLKDJASLKDJLASKJDLASJDLASJDLSAJLDAJSLKDAJSLKDJALKSJDALSD");
printf("name:%s define:%s \n",np->name,np->defn);
np=lookup("Z");
printf("name:%s define:%s \n",np->name,np->defn);
if(undef("Z")==1){//等于1表示成功undef
np=lookup("Z");
if(np!=NULL){
printf("name:%s define:%s \n",np->name,np->defn);
}else{
printf("不存在Z的deifine");
}
}
getchar();
return 0;
}
Exercise 6-6
Implement a simple version of the #define processor (i.e., no arguments) suitable for use with C programs, based on the routines of this section. You may also find getch and ungetch helpful.答:
暂时未研究