通过一个表查找的程序,深入讨论结构。
当遇到如宏定义 #define IN 1时,宏处理器和编译器如何处理呢。需要把名字IN和替换文本1存入某个表中,当出现IN时如“statet = IN;
就必须用1来替换IN。
1处理名字 需要将名字和替换文本存入表中.用函数install(s,t)来实现。遇到之后就用lookup(s)函数在表中查找,若找到,返回指向该处到指针;若没找到,返回NULL。
算法:采用散列查找方法(将输入的名字转换为一个小的非负整数,该整数随后作为一个指针数组的下标。每个数组每个元素指向某个链表的表头,链表中的各个块用于描述具有
该散列值的名字。如果没有名字散列到该值,则数组元素的值为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*/
lookup和Install函数都使用hash,通过一个for循环进行计算,每次循环,将上一次循环计算得到到结果经过变换(乘以31)后得到新的值同字符串当前字符的值相加(*s + 31 *
hashval),然后将该结果值同数组长度执行去模操作(即取余),其结果是该函数的返回值。这种散列函数不是最优,但简短有效。
/*hash: from hash value for string s* /
unsigned hash(char *s)
{
unsigned hashval;
for(hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
散列过程生产了在数组hastab中执行查找的起始下标.如果字符串可以被找到,就一定位于该起始下标指向的链表的某个块.查找过程由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; /*找到*/
return NULL; /*没找到*/
}
for( ptr = head;ptr != NULL; ptr = ptr -> next)是遍历一个链表的标准方法.
install函数借助lookup函数判断待加入的名字是否存在,同上,存在就用新的的定义替换,没有就创建一个新的表项,如果空间不足,返回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){ /*没有找到*/
np = (struct nlist *) malloc( size(* np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else
free((void *) np->defn);
if((np->defn = strdup(defn)) == NULL;
return NULL;
return np;
}