《C程序设计语言》(《The C Programming Language》)第二版第六章练习题

6-1:
上述getword函数不能正确处理下划线、字符串常量、注释及预处理器控制指令。请编写一个更完善的getword函数

/*原函数无法识别带下划线的字符串,例如keytab数组将if和else视为关键字,但是
如果我们输入if_else,这其实不是关键字,但是程序还会将其保留,我们需要排除
这种情况,同时对于""包裹的字符串常量、注释符包裹的注释语句、预处理器指令,
我们需要排除这些语句*/
#include 
#include 
#include 

#define BUFSIZE 100
#define MAXWORD 100
#define NKEYS (sizeof(keytab)/sizeof(keytab[0]))

char buf[BUFSIZE];
int bufp = 0;

struct key {
	char *word;
	int count;
};

int binsearch(char *, struct key *, int);
int getword(char *, int);

int main(void)
{
	char word[MAXWORD];
	int n;
	struct key keytab[] = {
		{"break", 0},
		{"continue", 0},
		{"else", 0},
		{"if", 0},
		{"struct", 0}	
	};

	while(getword(word, MAXWORD) != EOF)
		if(isalpha(word[0]))
			if((n = binsearch(word, keytab, NKEYS)) >= 0)
				keytab[n].count++;

	for(n = 0; n < NKEYS; n++)
		if(keytab[n].count > 0)
			printf("%4d %s\n", keytab[n].count, keytab[n].word);
	return 0;
}

int binsearch(char *word, struct key tab[], int n)
{
	int low, mid, high;

	low = 0;
	high = n - 1;
	while(low <= high)
	{
		mid = (low + high) / 2;
		if(strcmp(word, tab[mid].word) < 0)
			high = mid - 1;
		else if(strcmp(word, tab[mid].word) > 0)
			low = mid + 1;
		else
			return mid;
	}
	return -1;
}

int getword(char *word, int lim)
{
	int c, prev, getch(void);
	void ungetch(int);
	char *w = word;

	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalpha(c) && c != '_')//排除下划线在前的情况
	{
		if(c == '#')
		{
			while((c = getch()) != '\n')
				;
		}
		else if(c == '\"')
		{
			while((c = getch()) != '\"')
				;
		}
		else if(c == '/')
		{
			if((c = getch()) == '*')
			{
				prev = ' ';
				while((c = getch()) != EOF)//从遇到/*开始循环,直到EOF或者*/退出
				{
					if(c == '/' && prev == '*')
						break;
					prev = c;
				}
			}
			else if(c == '/')
			{
				while((c = getch()) != '\n')
					;
			}
			else
				ungetch(c);
		}

		else
			*w = '\0';
		return c;
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()) && *w != '_')//排除下划线在后的情况
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}

6-2:
编写一个程序,用以读入一个C语言程序,并按字母表顺序分组打印变量名,要求每一组内各变量名的前6个字符相同,其余字符不同。字符串和注释中的单词不予考虑。请将6作为一个可在命令行设定的参数

/*光看题目就有点晕,我理解的意思是:
建立一个二叉查找树(第一层),该树的每个节点本身又是一个二叉查找树(第二
层),将前6个字符相同的单词放在第一层树的一个节点中,然后在第二层树中再对
这些单词排序
*/
#include 
#include 
#include 
#include 

#define BUFSIZE 100
#define MAXWORD 100

char buf[BUFSIZE];
int bufp = 0;

struct tree2 {            //第二层树,存储前6个字符相同的变量名
	char *s;
	int count;
	struct tree2 *left;
	struct tree2 *right;
};

struct tree1 {           //第一层树,存储前6个字符不同的变量名
	char *s;
	struct tree1 *left;
	struct tree1 *right;
	struct tree2 *root;
};

int getword(char *, int);
struct tree1 *addtree1(struct tree1 *p, char *w, int n);
struct tree2 *addtree2(struct tree2 *p, char *w);
void treeprint2(struct tree2 *p);
void treeprint1(struct tree1 *p);
void *talloc(int n);
char *strdup(char *s, int n);
void freetree1(struct tree1 *p);
void freetree2(struct tree2 *p);


int main(int argc, char **argv)
{
	int n = 6;
	struct tree1 *root = NULL;
	char word[MAXWORD];

	if(argc != 2)
		printf("parameters error!\n");
	else
		n = atoi(argv[1]);

	while(getword(word, MAXWORD) != EOF)
		if(isalpha(word[0]))
			root = addtree1(root, word, n);

	treeprint1(root);
	freetree1(root);   //释放malloc函数分配的空间
	return 0;
}

struct tree1 *addtree1(struct tree1 *p, char *w, int n)
{
	int cond;

	if(p == NULL)
	{
		p = (struct tree1 *)talloc(1);
		p -> s = strdup(w, n);
		p -> left = p -> right = NULL;
		p -> root = NULL;
		p -> root = addtree2(p -> root, w);//将首个单词存入二层树树根
	}

	else if((cond = strncmp(w, p -> s, n)) == 0)
		p -> root = addtree2(p -> root, w);
	
	else if(cond > 0)
		p -> right = addtree1(p -> right, w, n);
	
	else
		p -> left = addtree1(p -> left, w, n);
	return p;
}

struct tree2 *addtree2(struct tree2 *p, char *w)
{
	int cond;

	if(p == NULL)
	{
		p = (struct tree2 *) talloc(2);
		p -> s = strdup(w, MAXWORD);
		p -> count = 1;
		p -> left = p -> right = NULL;
	}

	else if((cond = strcmp(w, p -> s)) == 0)
		p -> count++;
	
	else if(cond > 0)
		p -> right = addtree2(p -> right, w);
	
	else
		p -> left = addtree2(p -> left, w);
	return p;
}

void *talloc(int n)
{
	if(n == 1)
		return (struct tree1 *) malloc(sizeof(struct tree1));//返回指向一层树一个节点的指针
	else
		return (struct tree2 *) malloc(sizeof(struct tree2));//返回指向二层树一个节点的指针
}

char *strdup(char *s, int n)
{
	char *p;
	if(n == MAXWORD)
		p = (char *) malloc(strlen(s) + 1);
	else
		p = (char *) malloc(sizeof(char) * (n + 1));
	
	if(p != NULL)
	{
		*p = '\0';
		if(n == MAXWORD)
			strcpy(p, s);
		else
			strncat(p, s, n);
	}
	return p;
}

void treeprint1(struct tree1 *p)
{
	if(p != NULL)
	{
		treeprint1(p -> left);
		printf("%s: ", p -> s);
		treeprint2(p -> root);
		putchar('\n');
		treeprint1(p -> right);
	}
}

void treeprint2(struct tree2 *p)
{
	if(p != NULL)
	{
		treeprint2(p -> left);
		printf("%s (%d)  ", p -> s, p -> count);
		treeprint2(p -> right);
	}
}

void freetree1(struct tree1 *p)
{
	struct tree1 *pright;
	
	if(p != NULL)
	{
		pright = p -> right;
		freetree1(p -> left);
		freetree2(p -> root);
		free(p);
		freetree1(pright);
	}
}

void freetree2(struct tree2 *p)
{
	struct tree2 *pright;
	
	if(p != NULL)
	{
		pright = p -> right;
		freetree2(p -> left);
		free(p);
		freetree2(pright);
	}
}

int getword(char *word, int lim)
{
	int c, prev, getch(void);
	void ungetch(int);
	char *w = word;

	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalpha(c))
	{
		if(c == '\"')
		{
			while((c = getch()) != '\"')
				;
		}
		else if(c == '/')
		{
			if((c = getch()) == '*')
			{
				prev = ' ';
				while((c = getch()) != EOF)//从遇到/*开始循环,直到EOF或者退出
				{
					if(c == '/' && prev == '*')
						break;
					prev = c;
				}
			}
			else if(c == '/')
			{
				while((c = getch()) != '\n')
					;
			}
			else
				ungetch(c);
		}

		else
			*w = '\0';
		return c;
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}
//上面的addtree1和addtree2、freetree1和freetree2函数可以合并,但是涉及到
//强制类型转换、函数返回类型、传入参数类型和数量等,会比较复杂,所以简单起
//见就将它们分开了

6-3:
编写一个交叉引用程序,打印文档中所有单词的列表,并且每个单词还有一个列表,记录出现过该单词的行号。对the、and等非实义单词不予考虑

/*此题采用一个二叉树加一个链表的形式储存单词和它出现的行号,链表可以视为单
子树的二叉树,即只有一个指向下一节点的指针*/
#include 
#include 
#include 
#include 

#define BUFSIZE 100
#define MAXWORD 100

char buf[BUFSIZE];
int bufp = 0;

struct tree {
	char *s;
	int count;
	struct list *head;//指向链表表头的指针
	struct tree *left;
	struct tree *right;
};

struct list {
	int line;
	struct list *next;
};

int getword(char *, int);
struct tree *addtree(struct tree *p, char *w, int n);
struct list *addlist(struct list *p, int n);
void treeprint(struct tree *p);
void listprint(struct list *p);
void *talloc(int n);
char *strdup(char *s);
void freetree(struct tree *p);
void freelist(struct list *p);


int main(void)
{
	struct tree *root = NULL;
	int line = 1;
	int c;
	char word[MAXWORD];

	while((c = getword(word, MAXWORD)) != EOF)
	{
		if(c == '\n')
		{
			line++;
		}
		if(isalpha(word[0]) && strstr("the and ", word) == NULL)
			root = addtree(root, word, line);
		
	}

	treeprint(root);
	freetree(root);   //释放malloc函数分配的空间
	return 0;
}

struct tree *addtree(struct tree *p, char *w, int n)
{
	int cond;

	if(p == NULL)
	{
		p = (struct tree *) talloc(1);
		p -> s = strdup(w);
		p -> count = 1;
		p -> left = p -> right = NULL;
		p -> head = NULL;
		p -> head = addlist(p -> head, n);//将首次出现的单词的行号存入链表表头
	}

	else if((cond = strcmp(w, p -> s)) == 0)
	{
		p -> count++;
		p -> head = addlist(p -> head, n);
	}
	
	else if(cond > 0)
		p -> right = addtree(p -> right, w, n);
	
	else
		p -> left = addtree(p -> left, w, n);
	return p;
}

struct list *addlist(struct list *p, int n)
{
	if(p == NULL)
	{
		p = (struct list *) talloc(2);
		p -> line = n;
		p -> next = NULL;
	}
	else
		p -> next = addlist(p -> next, n);
	return p;
}

void *talloc(int n)
{
	if(n == 1)
		return (struct tree *) malloc(sizeof(struct tree));
	else
		return (struct list *) malloc(sizeof(struct list));
}

char *strdup(char *s)
{
	char *p;

	p = (char *) malloc(strlen(s) + 1);

	if(p != NULL)
		strcpy(p, s);
	return p;
}

void treeprint(struct tree *p)
{
	if(p != NULL)
	{
		treeprint(p -> left);
		printf("%s(%d): ", p -> s, p -> count);
		listprint(p -> head);
		putchar('\n');
		treeprint(p -> right);
	}
}

void listprint(struct list *p)
{
	if(p != NULL)
	{
		printf("%d ", p -> line);
		listprint(p -> next);
	}
}

void freetree(struct tree *p)
{
	struct tree *pright;
	
	if(p != NULL)
	{
		pright = p -> right;
		freetree(p -> left);
		freelist(p -> head);
		free(p);
		freetree(pright);
	}
}

void freelist(struct list *p)
{
	if(p -> next != NULL)
		freelist(p -> next);
	free(p);
}

int getword(char *word, int lim)
{
	int c, prev, getch(void);
	void ungetch(int);
	char *w = word;

	while((c = getch()) == ' ' || c == '\t')
		;
	if(c != EOF)
		*w++ = c;
	if(!isalpha(c))
	{
		*w = '\0';
		return c;
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}

6-4:
编写一个程序,根据单词的出现频率按降序打印输入的各个不同单词,并在每个单词的前面标上它的出现次数

/*此题我能想到的有三种方法:
一、以链表的形式储存单词和它的出现次数这些信息,再以出现频率顺序交换这些信
息,再打印,但是在单词的储存过程中会很耗费时间
二、以二叉查找树的形式储存信息,再按顺序交换这些信息,但是具体的实现方法很
复杂,而且程序耗费的时间也是一个问题
三、以二叉查找树的形式储存信息,再创建一个指针数组,数组中的每个指针指向一
个二叉树节点,再对这些指针进行排序,相对前两种方法,其储存单词时间和排序时
间更少,所以采用这种方法*/
#include 
#include 
#include 
#include 

#define BUFSIZE 100
#define MAXWORD 100

char buf[BUFSIZE];
int bufp = 0;
int node = 0; //计算树中的节点个数,声明为外部变量
struct tree **tree_node;//指针数组,每个指针指向一个树节点

struct tree {
	char *s;
	int count;
	struct tree *left;
	struct tree *right;
};


int getword(char *, int);
struct tree *addtree(struct tree *p, char *w);
void treeprint(struct tree *p);
void *talloc(int n);
char *strdup(char *s);
void freetree(struct tree *p);
void qsort(struct tree **s, int left, int right);
void swap(struct tree **v, int i, int j);



int main(void)
{
	struct tree *root = NULL;
	char word[MAXWORD];

	while(getword(word, MAXWORD) != EOF)
		if(isalpha(word[0]))
			root = addtree(root, word);

	tree_node = (struct tree **) talloc(2);//为指针数组分配空间
	
	printf("按字母表顺序排序单词\n");
	treeprint(root);//为每个指针赋值
	putchar('\n');
	
	printf("按出现频率排序单词\n");
	qsort(tree_node, 0, node - 1);//为指针数组排序
	for(int i = 0; i < node; i++)
	{
		printf("(%d)%s\n", tree_node[i] -> count, tree_node[i] -> s);
	}
	
	freetree(root);   //释放malloc函数分配的空间
	free(tree_node);
	return 0;
}

struct tree *addtree(struct tree *p, char *w)
{
	int cond;

	if(p == NULL)
	{
		p = (struct tree *)talloc(1);
		p -> s = strdup(w);
		p -> count = 1;
		p -> left = p -> right = NULL;
		node++;//统计节点个数
	}

	else if((cond = strcmp(w, p -> s)) == 0)
		p -> count++;
	
	else if(cond > 0)
		p -> right = addtree(p -> right, w);
	
	else
		p -> left = addtree(p -> left, w);
	return p;
}


void *talloc(int n)
{
	if(n == 1)
		return (struct tree *) malloc(sizeof(struct tree));
	else
		return (struct tree **) malloc(sizeof(struct tree *) * node);
}

char *strdup(char *s)
{
	char *p;

	p = (char *) malloc(strlen(s) + 1);

	if(p != NULL)
		strcpy(p, s);
	return p;
}

void treeprint(struct tree *p)
{
	static struct tree **s = tree_node;//此函数不能采用传递tree_node为指针数组的每个指针赋值,
	//因为递归调用会造成混乱,新建一个static变量,防止重复初始化

	if(p != NULL)
	{
		treeprint(p -> left);
		printf("(%d) %s\n", p -> count, p -> s);
		*s++ = p;
		treeprint(p -> right);
	}
}

void qsort(struct tree **s, int left, int right)
{
	int i, last;
	if(left >= right)
		return;

	swap(s, left, (left + right) / 2);
	last = left;

	for(i = left + 1; i <= right; i++)
		if(s[i] -> count > s[left] -> count)
			swap(s, ++last, i);
	
	swap(s, left, last);
	qsort(s, left, last - 1);
	qsort(s, last + 1, right);
}

void swap(struct tree **v, int i, int j)
{
	struct tree *temp;

	temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}

void freetree(struct tree *p)
{
	struct tree *pright;
	
	if(p != NULL)
	{
		pright = p -> right;
		freetree(p -> left);
		free(p);
		freetree(pright);
	}
}

int getword(char *word, int lim)
{
	int c, prev, getch(void);
	void ungetch(int);
	char *w = word;

	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalpha(c))
	{
		*w = '\0';
		return c;
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}

6-5:
编写函数undef,它将从由lookup和install维护的表中删除一个变量及其定义

/*在ASCII码中,每个字符都可以用一个数字代替,那么一个字符串同样可以转换为
一个数字,通过一个函数将一个字符串转换为合适大小的数字,这个函数成为哈希函
数,而这个数字就是存储这条字符串的数组的下标,下一次再遇到此字符串时,利用
哈希函数可得到相同的数字,查找这个数字下标的数组元素就可以找到之前存储的字
符串,这就是散列查找,也称哈希查找。哈希查找的时间复杂度为O(1),因为只需要
利用哈希函数得到数组下标就可以通过该下标查找到元素
但是哈希查找也有缺陷,易出现冲突,即两个不相同的字符串可能通过哈希函数得到
的数字相同,尤其是数组规模不大时,这种冲突极易出现,解决冲突的方式有多种,
此书采用的是链表法,即数组的每个元素是一个指向链表的指针,在链表中存放字符
串,当遇到冲突字符串时,将冲突字符串添加到链表中,查找时遍历整个链表,寻找
是否有相同字符串*/
#include 
#include 
#include 
#include 

#define HASHSIZE 101
#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

struct nlist {
	struct nlist *next;
	char *name;
	char *defn;
};

static struct nlist *hashtab[HASHSIZE];

unsigned hash(char *s);
struct nlist *lookup(char *s);
struct nlist *install(char *name, char *defn);
int undef(char *name);
char *strdup(char *s);
int getword(char *word, int lim);
int getch(void);
void ungetch(int c);

int main(void)
{
	char name[100], define[100];
	struct nlist *np;

	printf("请输入创建表的名字\n");
	while(getword(name, 100) != EOF)
	{
		printf("请输入该名字的替换文本\n");
		getword(define, 100);
		if(install(name, define) == NULL)
			break;
		printf("继续输入名字\n");
	}

	printf("请输入待查找的名字\n");
	while(getword(name, 100) != EOF)
	{
		if((np = lookup(name)) != NULL)
			printf("%s %s\n", np -> name, np -> defn);
		else
			printf("error\n");
	}

	printf("请输入待删除的名称\n");
	while(getword(name, 100) != EOF)
	{
		if(undef(name))
			printf("删除成功\n");
		else
			printf("error\n");
	}

	printf("请输入待查找的名字\n");
	while(getword(name, 100) != EOF)
	{
		if((np = lookup(name)) != NULL)
			printf("%s %s\n", np -> name, np -> defn);
		else
			printf("error\n");
	}

	return 0;
}

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;

	return NULL;
}

struct nlist *install(char *name, char *defn)
{
	struct nlist *np;
	unsigned hashval;

	if((np = lookup(name)) == NULL)//查找链表有无匹配的名字,如果没有
	{
		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]指向的是原链表的表头
		hashtab[hashval] = np;//np现在成为新链表的表头,hashtab[hashval]指向np
	}
	else
		free((void *) np -> defn);//释放原替换文本分配的空间

	if((np -> defn = strdup(defn)) == NULL)//不管链表有无匹配的名字,都将通过此语句为defn分配空间并赋值
		return NULL;

	return np;
}

int undef(char *name)
{
	struct nlist *np1, *np2;

	for(np1 = hashtab[hash(name)], np2 = NULL; np1 != NULL; np2 = np1, np1 = np1 -> next)
		if(!strcmp(name, np1 -> name))
		{
			if(np2 == NULL)
				hashtab[hash(name)] = np1 -> next;
			else
				np2 -> next = np1 -> next;
			free(np1 -> name);	
	        free(np1 -> defn);
	        free(np1);
	        return 1;
		}
	return 0;
}

char *strdup(char *s)
{
	char *p;

	p = (char *) malloc(strlen(s) + 1);

	if(p != NULL)
		strcpy(p, s);
	return p;
}

int getword(char *word, int lim)
{
	int c, getch(void);
	void ungetch(int);
	char *w = word;

	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalnum(c))
	{
		*w = '\0';
		return c;
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}

6-6:
以本节介绍的函数为基础,编写一个适合C语言程序使用的#define处理器的简单版本(即无参数的情况下)。你会发现getch和ungetch函数非常有用

/*简单版本的程序,很多情况忽略了,如注释或者""中出现了宏时,仍然会执行替换*/
#include 
#include 
#include 
#include 

#define HASHSIZE 101
#define BUFSIZE 100
#define MAXWORD 100



char buf[BUFSIZE];
int bufp = 0;
char name[MAXWORD];
char defn[MAXWORD];

struct nlist {
	struct nlist *next;
	char *name;
	char *defn;
};

static struct nlist *hashtab[HASHSIZE];

unsigned hash(char *s);
struct nlist *lookup(char *s);
struct nlist *install(char *name, char *defn);
int undef(char *name);
char *strdup(char *s);
int getword(char *word, int lim);
int getch(void);
void ungetch(int c);
void skipblanks(void);



int main()
{
	char word[MAXWORD];
	struct nlist *np;

	while(getword(word, MAXWORD) != EOF)
	{
		if(isalpha(word[0]) || word[0] == '_')
		{
			if((np = lookup(word)) != NULL)
				printf("%s", np -> defn);
			else
				printf("%s", word);
		}
		else
			printf("%s", word);
	}

	return 0;		
}

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;

	return NULL;
}

struct nlist *install(char *name, char *defn)
{
	struct nlist *np;
	unsigned hashval;

	if((np = lookup(name)) == NULL)//查找链表有无匹配的名字,如果没有
	{
		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]指向的是原链表的表头
		hashtab[hashval] = np;//np现在成为新链表的表头,hashtab[hashval]指向np
	}
	else
		free((void *) np -> defn);//释放原替换文本分配的空间

	if((np -> defn = strdup(defn)) == NULL)//不管链表有无匹配的名字,都将通过此语句为defn分配空间并赋值
		return NULL;

	return np;
}

int undef(char *name)
{
	struct nlist *np1, *np2;

	for(np1 = hashtab[hash(name)], np2 = NULL; np1 != NULL; np2 = np1, np1 = np1 -> next)
		if(!strcmp(name, np1 -> name))
		{
			if(np2 == NULL)
				hashtab[hash(name)] = np1 -> next;
			else
				np2 -> next = np1 -> next;
			free(np1 -> name);	
	        free(np1 -> defn);
	        free(np1);
	        return 1;
		}
	return 0;
}

char *strdup(char *s)
{
	char *p;

	p = (char *) malloc(strlen(s) + 1);

	if(p != NULL)
		strcpy(p, s);
	return p;
}

int getword(char *word, int lim)
{
	int c, getch(void);
	void ungetch(int);
	char *w = word;
	struct nlist *np;

	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalnum(c))
	{
		if(c == '#')
		{
			w = word;
			for(; --lim > 0 && isalpha(c = getch()); *w++ = c)
				;
			*w = '\0';
			skipblanks();

			if(!strcmp(word, "define"))//处理#define命令
			{
				for(int i = 0; i < MAXWORD && (isalnum(c = getch()) || c == '_'); i++)
				{
					name[i] = c;
					if(isdigit(name[0]))//排除首字符为字母的情况
						printf("error: the first character cannot be a number\n");
				}
				name[i] = '\0';
				
				skipblanks();
				for(i = 0; i < MAXWORD; i++)
				{
					defn[i] = getch();
					if(defn[i] == '\n' && defn[i - 1] != '\\')//允许使用'\'字符换行的情况
						break;
				}
				defn[i] = '\0';
				install(name, defn);
				word[0] = '\0';
			}

			else if(!strcmp(word, "undef"))//处理#undef
			{
				for(int i = 0; i < MAXWORD && (isalnum(c = getch()) || c == '_'); i++)
				{
					name[i] = c;
					if(isdigit(name[0]))
						printf("error: the first character cannot be a number\n");
				}
				name[i] = '\0';
				if(!undef(name))
					printf("error: The name was not found");
				word[0] = '\0';
			}

			else//处理#加宏名的情况,使用""包裹替换文本取代
			{
				if((np = lookup(word)) != NULL)
				{
					strcpy(word, "\"");
					strcat(word, np -> defn);
					strcat(word, "\"");
				}
				else
					printf("error: invalid instruction\n");
			}

			return 0;
		}
		else
		{
			*w = '\0';
			return c;
		}
	}
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: too many parameters\n");
	else
		buf[bufp++] = c;
}

void skipblanks(void)
{
	int c;
	while((c=getch())==' '||c=='\t')
			;
	ungetch(c);
}

你可能感兴趣的:(c语言)