1.6 数组
让我们编写一个程序来统计每个数字、空白字符(空格、制表符和换行符)和所有其他的字符出现的次数。这是人为的,但是它让我们在一个程序里说明了C语言里的一些方面。
有12类输入,所以使用一个数组来记录每个数字出现的次数则更为方便,而不用十个独立的变量。这是程序的一个版本:
#include <stdio.h>
/* count digits, white space, others */
main()
{
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; ++i)
ndigit[i] = 0;
while ((c = getchar()) != EOF)
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if (c == ' ' || c == '/n' || c == '/t')
++nwhite;
else
++nother;
printf("digits =");
for (i = 0; i < 10; ++i)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d/n",
nwhite, nother);
}
这个程序的一个输出是:
digits = 9 3 0 0 0 0 0 0 0 1, white space = 123, other = 345
这条声明语句:
int ndigit[10];
声明了ndigit为一个10个整数的数组。C语言中的数组下标总是从0开始的,所以数组的元素为:ndigit[0], ndigit[1], ..., ndigit[9]。这在初始化和打印该数组的for循环中得到了体现。
下标可以是任何整型表达式,包括整型变量,如i,和整型常量。
这个特殊的程序依赖于数字的字符表示的特性。例如,这条测试:
if (c >= '0' && c <= '9')
判断字符‘c’是否是一个数字。如果是,则那个数字的数据值是:
c - '0'
这仅会在'0', '1', ..., '9'有连续递增的值时才有用。幸运的是,所有的字符集都是这样的。
通过定义,char类型只是小的整型数,所以char变量和常量在算术表达式中和int类型是相同的。这很自然并且方便,例如c - ‘0’是一个值在0到9之间的整型表达式,该值对应于存储在’c’中的字符‘0’和‘9’,数组ndigit的有效下标也是这样。
关于一个字符是否为一个数字、空白符或什么别的东西的判断是由如下语句序列来完成的:
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if (c == ' ' || c == '/n' || c == '/t')
++nwhite;
else
++nother;
这种文法:
if (condition1)
statement1
else if (condition2)
statement2
...
...
else
statementn
在程序中经常出现,作为表达一个多向判断的方式。所有的condition从顶部按顺序进行判断,直到某个condition得到满足,此时对应的statement部分被执行,之后整个结构就结束了。(任何的statement都可以是在大括号中的多条语句。)如果所有的条件都不满足,并存在最后的else之后的statement,则执行它。当在单词统计程序中,如果最后的else和statement都被省略了,则不会发生任何的动作。可以有任意多的:
else if(condition)
statement;
组,它们在初始的if和最后的else中间。
作为一种风格,我们建议应该向我们展示的那样格式化这种结构,如果每个if相对前一个else缩进一点,则一个长序列的判断可能会跑到页面的右半部分。
switch语句,将会在第四章讨论,提供了另一种方式来编写多向分支,这特别适合于当条件是是否某个整型或字符表达式匹配某个常量集合中的一个值的情况。作为对比,我们将在3.4节中展示此程序的一个switch版本。
练习1-13:编写一个程序,来打印它的输入中的单词长度的柱状图。很容易使用水平条来绘制柱状图,垂直定向则更具挑战性。
练习1-14:编写一个程序,来打印它的输入中的不同字符出现频率的柱状图。