赞
踩
考虑写个程序来计算每个 C 语言关键字的出现次数。我们需要一个字符串数组来保存关键字名称,还需要一个整数数组来保存数量。一种可能的方式是使用两个并行的数组,keyword 和 keycount :
- char *keyword[NKEYS];
- int keycount[NKEYS];
但两个数组是并行的这个事实,暗示了还有一种不同的组织方式,即结构体数组。每个关键字条目是一对:
- char *word;
- int count;
且有一个数组由这样的对组成。如下结构声明
- struct key {
- char *word;
- int count;
- } keytab[NKEYS];
声明了一个结构体类型 key,定义了这种类型的一个数组 keytab,并为它们分配了空间。数组的每个元素都是一个结构体。也可以写成:
- struct key {
- char *word;
- int count;
- };
-
- struct key keytab[NKEYS];
由于 keytab 这个结构包含了名称的常量集合,最简单的办法是让它成为一个外部变量,并在定义时将它一劳永逸地初始化。结构体的初始化与之前的类似——在定义后面加上由大括号括起来的初始化列表:
- struct key {
- char *word;
- int count;
- } keytab[] = {
- "auto", 0,
- "break", 0,
- "case", 0,
- "char", 0
- "const", 0,
- "continue", 0,
- "default", 0,
- /* ... */
- "unsigned", 0,
- "void", 0,
- "volatile", 0,
- "while", 0
- };
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
初始化表达式是成对出现的,对应结构体的两个成员。更精确的方式是把“每行”或者说每个结构体都用大括号括起来,例如
- {"auto", 0},
- {"break", 0},
- {"case", 0}
- ...
但当初始化表达式是简单变量或字符串,而且都存在的情况下,内部的括号是不需要的。与简单类型的数组一样,如果存在初始化表达式,而且 [ ] 内为空时,结构体数组 keytab 中的条目个数将会由编译器自动计算。
关键字计算程序从 keytab 的定义开始。主例程通过反复调用 getword 函数来读取输入,getword 每次获取一个单词。每个单词都会在 keytab 中搜索,用的是我们在第三章写的二分搜索函数的字符串版本。表中的关键字必须要以升序存储。
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
-
- #define MAXWORD 100
-
- int getword(char *, int);
- int binserarch(char *, struct key *, int);
-
- /* 计算C关键字数量 */
- main()
- {
- int n;
- char word[MAXWORD];
-
- 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].count);
- return 0;
- }
-
-
-
- /* binsearch: 在 tab[0]...tab[n-1]中查找word */
- int binsearch(char *word, struct key tab[], int n)
- {
- int cond;
- int low, high, mid;
-
- low = 0;
- high = n - 1;
- while (low <= high) {
- mid = (low + high) / 2;
- if (cond = strcmp(word, tab[mid].word) < 0){
- low = mid + 1;
- else if (cond > 0)
- high = mid -1;
- else
- return mid;
- }
- return -1;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
我们待会来展示 getword 函数;现在只要知道每次调用 getword 返回一个单词,该单词会被拷贝函数的第一个参数(字符数组)中,这就足够了。
NKEYS 是 keytab 中关键字的数量。尽管我们可以手动计算,但让机器来做会更简单而且更安全,特别是这个列表还可能会变化。一种可能的方法是用一个空指针来结束初始化列表,然后在 keytab 中循环,直到遇到结尾。
但没必要做那么多,因为数组的大小完全是在编译期间确定的。数组的大小是每个元素的大小乘以元素的个数,因此元素的个数就是
keytab 的大小 / struct key 的大小
C 语言提供了一个编译期的一元操作符 sizeof 用来计算任意对象的大小。表达式
sizeof 对象
或
sizeof 类型名
会得到一个整数,等于指定对象或类型所占空间的字节数。(严格地说,sizeof 产生的是无符号整数,其类型为 size_t,在 <stddef.h> 头文件中定义。)对象可以是变量或数组或结构体,类型名可以是基本类型如 int 或 double的名字,也可以是派生类型如结构体或指针的名字。
在我们的例子中,关键字的数量是数组的大小除以一个元素的大小。使用一个 #define 语句来计算并设置 NKEYS 的值。
#define NKEYS (sizeof keytab / sizeof (struct key))
另一种方法是用数组的大小除以某个特定元素的大小
#define NKEYS (sizeof keytab / sizeof keytab[0])
后者的优势在于,如果数组类型变化,这个定义也不用修改。
sizeof 不能用在 #if 行中,因为预处理器不会解析类型名。但 #define 中的表达式不是由预处理器求值,所以这里的代码是合法的。
现在来看看 getword 函数。我们写了一个比这个程序所需更为通用的 getword 函数,但并不复杂。getword 从 输入中获取下一个“单词”,其中单词是一个由字母开头的字母或数字的字符串,或者单个非空格字符。函数的值是单词的首字母,或者是代表文件结束的EOF,或者是字符本身,如果该字符不是字母的话。
- /* getword:从输入中获取下一个单词或字符 */
- int getword(char *word, int limit)
- {
- 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 (!isalphanum(*w = getch())) {
- ungetch(*w);
- break;
- }
- *w = '\0';
- return word[0];
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
getword 使用了我们在第四章写的 getch 和 ungetch 函数。当字母和数字组成的 token 结束时,getword 多读了一个字符。调用 ungetch 把这个字符推回给输入,供下一次调用使用。getword 还使用了 isspace 来跳过空白字符,isalpha 来识别字母,isalphanum来识别字母和数字;这些函数都来自标准头文件 <ctype.h>。
练习6-1、我们这个版本的 getword 没有正确处理下划线,字符串常量,注释,以及预处理控制行。写个更好的版本。
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。