Structures
2017-02-19 15:22 星星之火✨🔥 阅读(537) 评论(0) 收藏 举报1、在TCPL Reading Notes 中第79 条中,getword 函数不能正确处理下划线、字符串常量、注释及预处理器控制指令。请编写一个更完善的getword 函数。
#include <stdio.h>
#include <ctype.h>
// getword: get next word or character from input
int getword(char *word, int lim)
{
int c, d, comment(void), getch(void);
void ungetch(int);
char *w = word;
while(isspace(c = getch()))
;
if(c != EOF)
*w++ = c;
if(isalpha(c) || c == '_' || c == '#')
{
for(; --lim > 0; w++)
if(!isalnum(*w = getch()) && *w != '_')
{
ungetch(*w);
break;
}
}
else if(c == '\'' || c == '"')
{
for(; --lim > 0; w++)
if((*w = getch()) == '\\')
*++w = getch();
else if(*w == c)
{
w++;
break;
}
else if(*w == EOF)
break;
}
else if(c == '/')
if((d = getch()) == '*')
c = comment();
else
ungetch(d);
*w = '\0';
return c;
}
// comment: skip over comments and return a character
int comment(void)
{
int c;
while((c = getch()) != EOF)
if(c == '*')
if((c = getch()) == '/')
break;
else
ungetch(c);
return c;
}
为了处理下划线和预编译器指令,我们把原来的if(!isalpha(c)) 语句修改为if(isalpha(c) || c == '_' || c == '#'),跟在_ 和# 后面的字母和数字字符将被看作是单词的一部分。
字符串常量可能出现在单引号或双引号中,一旦检测到左引号,我们就把该左引号到与之邻近的右引号或EOF 标记之间的全部字符收集起来,并把它们当作一个字符串常量来对待。
如果遇到注释,我们就跳过其内容并返回它的结尾斜线字符。
2、编写一个程序,根据单词的出现频率按降序打印输入的各个不同单词,并在每个单词的前面标上它的出现次数。
#include <stdio.h>
#include <ctype.h>
#define MAXWORD 100
#define NDISTINCT 1000
struct tnode {
char *word; // the tree node;
int count; // points to the text
struct tnode *left; // left child
struct tnode *right; // right child
};
struct tnode *addtree(struct tnode *, char *);
int getword(char *, int);
void sortlist(void);
void treestore(struct tnode *);
struct tnode *list[NDISTINCT]; // pointers to tree nodes
int ntn = 0; // number of tree nodes
// print distinct words sorted in decreasing order of freq.
int main(void)
{
struct tnode *root;
char word[MAXWORD];
int i;
root = NULL;
while(getword(word, MAXWORD) != EOF)
if(isalpha(word[0]))
root = addtree(root, word);
treestore(root);
sortlist();
for(i = 0; i < ntn; i++)
printf("%2d:%20s\n", list[i]->count, list[i]->word);
return 0;
}
struct tnode *talloc(void); // make a tnode
char *strdup(char *); // make a duplicate of s
// addtree: add a node with w, at or below p
struct tnode *addtree(struct tnode *p, char *w)
{
int cond;
if(p == NULL) // a new word has arrived
{
p = talloc(); // make a new node
p->word = strdup(w);
p->count = 1;
p->left = p->right = NULL;
}
else if((cond = strcmp(w, p->word)) == 0)
p->count++; // repeated word
else if(cond < 0) // less than into left subtree
p->left = addtree(p->left, w);
else // greater than into right subtree
p->right = addtree(p->right, w);
return p;
}
// treestore: store in list[] pointers to tree nodes
void treestore(struct tnode *p)
{
if(p != NULL)
{
treestore(p->left);
if(ntn < NDISTINCT)
list[ntn++] = p;
treestore(p->right);
}
}
// sortlist: sort list of pointers to tree nodes
void sortlist()
{
int gap, i, j;
struct tnode *temp;
for(gap = ntn/2; gap > 0; gap /= 2)
for(i = gap; i < ntn; i++)
for(j = i-gap; j >= 0; j -= gap)
{
if((list[j]->count) >= (list[j+gap]->count))
break;
temp = list[j];
list[j] = list[j+gap];
list[j+gap] = temp;
}
}
// getword: get next word or character from input
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(!isalpha(c))
{
*w = '\0';
return c;
}
for(; --lim > 0; w++)
if(!isalnum(*w = getch()))
{
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
#include <stdlib.h>
// talloc: make a tnode
struct tnode *talloc(void)
{
return (struct tnode *)malloc(sizeof(struct tnode));
}
// strdup: make a duplicate of s
char *strdup(char *s)
{
char *p;
p = (char *)malloc(strlen(s) + 1); // +1 for '\0'
if(p != NULL)
strcpy(p, s);
return p;
}
常数NDISTINCT 对不同单词的最大数目进行了限制。list 是一个指针数组,其中的每个指针都指向一个tnode 类型的结构。变量ntn在treestore 函数运行结束后保存了树节点的个数。程序读入每个单词并把它放到树中。函数treestore 把那些指向tnode 结构的指针保存到数组list 中。函数sortlist 是shellsort 函数的改进版本,它按单词出现次数由高到低的顺序对数组list 进行排序。
3、针对TCPL Reading Notes 中第82 条散列表查找编写一个函数,它将从由lookup 和install 维护的表中删除一个变量名及其定义。
unsigned hash(char *);
// undef: remove a name and definition from the table
void undef(char *s)
{
int h;
struct nlist *prev, *np;
prev = NULL;
h = hash(s); // hash value of string s
for(np = hashtab[h]; np != NULL; np = np->next)
{
if(strcmp(s, np->name) == 0)
break;
prev = np; // remember previous entry
}
if(np != NULL) // found name
{
if(prev == NULL) // first in the hash list ?
hashtable[h] = np->next;
else // elsewhere in the hash list
prev->next = np->next;
free((void *)np->name);
free((void *)np->defn);
free((void *)np); // free allocated structure
}
}
函数undef 将在表中查找字符串s。当undef 找到字符串时,它将跳出for 循环,如果字符串s 不在表中,循环将在指针np 变成NULL 时终止。
指针数组hashtab 中的各个元素分别指向一个链表的开头。如果指针不为NULL,就说明其所指向的那个表中存在一组符合清除要求的变量名和定义;此时,指针np 指向将被清除的那个数据项,而指针prev 则指向出现在np 位置之前的那个数据项。如果prev 是NULL,就说明np 是以hashtab[h] 开头的那个链表的第一个元素。
在清除了np 所指的数据项之后,我们还有通过free 函数把该数据项的名字、定义及其存储结构都释放。
浙公网安备 33010602011771号