通過一個表查找的程序,深入討論結構。
當遇到如宏定義 #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;
}