表查找
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
表查找
为了对结构的更多方面进行深入的讨论,我们来编写一个表查找程序包的核心部分代码。
这段代码很典型,可以在宏处理器或编译器的符号表管理例程中找到。例如,考虑#define 语句。当遇到类似于
#define IN 1
之类的程序行时,就需要把名字IN和替换文本1存入到某个表中。此后,当名字IN出现在某些语句中时,如:
statet = IN;
就必须用1来替换IN。
以下两个函数用来处理名字和替换文本。install(s, t)函数将名字s 和替换文本t
记录到某个表中,其中s和t仅仅是字符串。lookup(s)函数在表中查找s,若找到,则返回指向该处的指针;若没找到,则返回NULL。
该算法采用的是散列查找方法——将输入的名字转换为一个小的非负整数,该整数随后
将作为一个指针数组的下标。数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字。如果没有名字散列到该值,则数组元素的值为NULL(参见图6-4)。图6-4
链表中的每个块都是一个结构,它包含一个指向名字的指针、一个指向替换文本的指针
以及一个指向该链表后继块的指针。如果指向链表后继块的指针为NULL,则表明链表结束。struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
相应的指针数组定义如下:
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
散列函数hash 在lookup 和install 函数中都被用到,它通过一个for 循环进行计
算,每次循环中,它将上一次循环中计算得到的结果值经过变换(即乘以31)后得到的新值同字符串中当前字符的值相加(*s + 31 * hashval),然后将该结果值同数组长度执行取
模操作,其结果即是该函数的返回值。这并不是最好的散列函数,但比较简短有效。
/* hash: form hash value for string s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
由于在散列计算时采用的是无符号算术运算,因此保证了散列值非负。
散列过程生成了在数组hashtab中执行查找的起始下标。如果该字符串可以被查找到,
则它一定位于该起始下标指向的链表的某个块中。具体查找过程由lookup 函数实现。如果
lookup函数发现表项已存在,则返回指向该表项的指针,否则返回NULL。
/* lookup: look for s in hashtab */
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 */
}
lookup函数中的for循环是遍历一个链表的标准方法,如下所示:
for (ptr = head; ptr != NULL; ptr = ptr->next)
...
install函数借助lookup函数判断待加入的名字是否已经存在。如果已存在,则用新
的定义取而代之;否则,创建一个新表项。如无足够空间创建新表项,则install函数返回
NULL。
struct nlist *lookup(char *);
char *strdup(char *);
/* install: put (name, defn) in hashtab */
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];
hashtab[hashval] = np;
} else /* already there */
free((void *) np->defn); /*free previous defn */
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
练习6-5 编写函数undef,它将从由lookup和install维护的表中删除一个变量
及其定义。
练习6-6 以本节介绍的函数为基础,编写一个适合C 语言程序使用的#define 处理
器的简单版本(即无参数的情况)。你会发现getch和ungetch函数非常有用。