当前位置:   article > 正文

C语言K&R圣经笔记 6.3结构体数组

C语言K&R圣经笔记 6.3结构体数组

6.3 结构体数组

考虑写个程序来计算每个 C 语言关键字的出现次数。我们需要一个字符串数组来保存关键字名称,还需要一个整数数组来保存数量。一种可能的方式是使用两个并行的数组,keyword 和 keycount :

  1. char *keyword[NKEYS];
  2. int keycount[NKEYS];

但两个数组是并行的这个事实,暗示了还有一种不同的组织方式,即结构体数组。每个关键字条目是一对:

  1. char *word;
  2. int count;

且有一个数组由这样的对组成。如下结构声明

  1. struct key {
  2. char *word;
  3. int count;
  4. } keytab[NKEYS];

声明了一个结构体类型 key,定义了这种类型的一个数组 keytab,并为它们分配了空间。数组的每个元素都是一个结构体。也可以写成:

  1. struct key {
  2. char *word;
  3. int count;
  4. };
  5. struct key keytab[NKEYS];

由于 keytab 这个结构包含了名称的常量集合,最简单的办法是让它成为一个外部变量,并在定义时将它一劳永逸地初始化。结构体的初始化与之前的类似——在定义后面加上由大括号括起来的初始化列表:

  1. struct key {
  2. char *word;
  3. int count;
  4. } keytab[] = {
  5. "auto", 0,
  6. "break", 0,
  7. "case", 0,
  8. "char", 0
  9. "const", 0,
  10. "continue", 0,
  11. "default", 0,
  12. /* ... */
  13. "unsigned", 0,
  14. "void", 0,
  15. "volatile", 0,
  16. "while", 0
  17. };

初始化表达式是成对出现的,对应结构体的两个成员。更精确的方式是把“每行”或者说每个结构体都用大括号括起来,例如

  1. {"auto", 0},
  2. {"break", 0},
  3. {"case", 0}
  4. ...

但当初始化表达式是简单变量或字符串,而且都存在的情况下,内部的括号是不需要的。与简单类型的数组一样,如果存在初始化表达式,而且 [ ] 内为空时,结构体数组 keytab 中的条目个数将会由编译器自动计算。

关键字计算程序从 keytab 的定义开始。主例程通过反复调用 getword 函数来读取输入,getword 每次获取一个单词。每个单词都会在 keytab 中搜索,用的是我们在第三章写的二分搜索函数的字符串版本。表中的关键字必须要以升序存储。

  1. #include <stdio.h>
  2. #include <ctype.h>
  3. #include <string.h>
  4. #define MAXWORD 100
  5. int getword(char *, int);
  6. int binserarch(char *, struct key *, int);
  7. /* 计算C关键字数量 */
  8. main()
  9. {
  10. int n;
  11. char word[MAXWORD];
  12. while (getword(word, MAXWORD) != EOF)
  13. if (isalpha(word[0]))
  14. if ((n = binsearch(word, keytab, NKEYS)) >= 0)
  15. keytab[n].count++;
  16. for (n = 0; n < NKEYS; n++)
  17. if (keytab[n].count > 0)
  18. printf("%4d %s\n",
  19. keytab[n].count, keytab[n].count);
  20. return 0;
  21. }
  22. /* binsearch: 在 tab[0]...tab[n-1]中查找word */
  23. int binsearch(char *word, struct key tab[], int n)
  24. {
  25. int cond;
  26. int low, high, mid;
  27. low = 0;
  28. high = n - 1;
  29. while (low <= high) {
  30. mid = (low + high) / 2;
  31. if (cond = strcmp(word, tab[mid].word) < 0){
  32. low = mid + 1;
  33. else if (cond > 0)
  34. high = mid -1;
  35. else
  36. return mid;
  37. }
  38. return -1;
  39. }

我们待会来展示 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,或者是字符本身,如果该字符不是字母的话。

  1. /* getword:从输入中获取下一个单词或字符 */
  2. int getword(char *word, int limit)
  3. {
  4. int c, getch(void);
  5. void ungetch(int);
  6. char *w = word;
  7. while (isspace(c = getch()))
  8. ;
  9. if (c != EOF)
  10. *w++ = c;
  11. if (!isalpha(c)) {
  12. *w = '\0';
  13. return c;
  14. }
  15. for ( ; --lim > 0; w++)
  16. if (!isalphanum(*w = getch())) {
  17. ungetch(*w);
  18. break;
  19. }
  20. *w = '\0';
  21. return word[0];
  22. }

getword 使用了我们在第四章写的 getch 和 ungetch 函数。当字母和数字组成的 token 结束时,getword 多读了一个字符。调用 ungetch 把这个字符推回给输入,供下一次调用使用。getword 还使用了 isspace 来跳过空白字符,isalpha 来识别字母,isalphanum来识别字母和数字;这些函数都来自标准头文件 <ctype.h>。

练习6-1、我们这个版本的 getword 没有正确处理下划线,字符串常量,注释,以及预处理控制行。写个更好的版本。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/50380
推荐阅读
相关标签
  

闽ICP备14008679号