#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF)
{
++nc;
if (c == 'n')
++nl;
if (c == ' ' || c == 'n' || c == 't')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %dn", nl, nw, nc);
}
程序执行时,每当遇到单词的第一个字符,它就作为一个新单词加以统计。state 变量记录程序当前是否正位于一个单词之中,它的初值是“不在单词中”,即初值被赋为 OUT。我们在这里使用了符号常量 IN 与 OUT,而没有使用其对应的数值 1 与 0,这样程序更易读。在较小的程序中,这种做法也许看不出有什么优势,但在较大的程序中,如果从一开始就这样做,因此而增加的一点工作量与提高程序可读性带来的好处相比是值得的。读者也会发现,如果程序中的幻数都以符号常量的形式出现,对程序进行大量修改就会相对容易得多。
下列语句 nl = nw = nc = 0; 将把其中的 3 个变量 nl、nw 与 nc 都设置为 0。这种用法很常见,但要注意这样一个事实:在兼有值与赋值两种功能的表达式中,赋值结合次序是由右至左。所以上面这条语句等同于 n1 = (nw = (nc = 0));
运算符||代表 OR(逻辑或),所以下列语句 if (c == ' ' || c== 'n' || c == 't') 的意义是“如果 c 是空格,或 c 是换行符,或 c 是制表符”(前面讲过,转义字符序列t 是制表符的可见表示形式)。相应地,运算符&&代表 AND(逻辑与),它仅比||高一个优先级。由&&或||连接的表达式由左至右求值,并保证在求值过程中只要能够判断最终的结果为真或假,求值就立即终止。如果 c 是空格,则没有必要再测试它是否为换行符或制表符,这样就不必执行后面两个测试。在这里,这一点并不特别重要,但在某些更复杂的情况下这样做就有必要了,不久我们将会看到这种例子。
这段程序中还包括一个 else 部分,它指定当 if 语句中的条件部分为假时所要执行的动作。其一般形式为:
if (表述式)
语句 1
else
语句 2
其中,if-else 中的两条语句有且仅有一条语句被执行。如果表达式的值为真,则执行语句 1,否则执行语句 2。这两条语句都既可以是单条语句,也可以是括在花括号内的语句序列。在单词计数程序中,else 之后的语句仍是一个 if 语句,该 if 语句控制了包含在花括号内的两条语句。










