c语言的库函数中提供了一些基本的字符输入和输出函数。其实,不管输入或输出的文本来自哪里,都是作为文本流来处理的。一个文本流包含一些字符,它们被换行符分成不同的行。当我们使用这些函数的时候,只需要考虑文本流的处理方式,而不用去管它来自哪里。
getchar和putchar
在这些库函数中,最基本最简单的就是getchar和putchar。调用getchar后,它从文本流中读取一个字符,并且返回它的值。
1 |
c = getchar(); |
这个语句执行后,c等于这条语句后的一个字符输入。它可能来自键盘,也可能来自文件。
putchar函数调用后显示一个字符。
File copying
只用getchar和putchar可以完成很多东西。
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> /* 用于将输入复制到输出的程序;第1个版本 */ main ( ) { int c; c = getchar ( ); while ( c != EOF ) { putchar ( c ); c = getchar ( ); } } |
其中的关系运算符! =的意思是“不等于”。
字符在计算机内部是以二进制的形式存储的。c h a r类型就是专门用于存储这种字符数据的类型,但是任何整数类型也可以用于存储字符数据。由于某种微妙却很重要的理由,此处使用了 i n t类型。
那么c语言怎么区别有没有输入完毕呢? C语言采取的解决方法是,g e t c h ar函数在没有输入时返回一个特殊值,这个特殊值不能与任何实际字符相混淆。这个值叫做E O F(end of file,文件结束)。必须把 c说明成一个大到足以存放 g e t c h a r函数可能返回的各种值的类型。之所以不把 c说明成c h a r类型,是因为c必须大到除了能存储任何可能的字符外还要能存储文件结束符 E O F。因此,把c说明成i n t类型的。
E O F是一个在 < s t d i o . h >库中定义的整数,但其具体的数值是什么并不重要,只要知道它与c h a r类型的所有值都不相同就行了。可以通过使用符号常量来保证 E O F在程序中不依赖于特定的数值。对于经验比较丰富的C程序员,可以把字符复制程序编写得更精致些。在 C语言中,诸如
1 |
c = getchar(); |
之类的赋值操作是一个表达式,因而就有一个值,即赋值后位于 =左边变量的值。换言之,赋值可以作为更大的表达式的一部分出现。可以把将字符赋给 c的赋值操作放在w h i l e循环语句的测试部分中,即可以将上面的字符复制程序改写成如下形式:
1 2 3 4 5 6 7 8 |
#include <stdio.h> /* 用于将输入复制到输出的程序;第2个版本 */ main ( ) { int c; while ( (c = getchar ( ) ) != EOF ) putchar ( c ); } |
在这一程序中,w h i l e循环语句先读一个字符并将其赋给 c,然后测试该字符是否为文件结束标记。如果该字符不是文件结束标记,那么就执行 w h i l e语句体,把该字符打印出来。再重复执行该w h i l e语句。当最后到达输入结束位置时, w h i l e循环语句终止执行,从而整个 m a i n程序执行结束。这个版本的特点是将输入集中处理—只调用了一次 g e t c h a r函数—这样使整个程序的规模有所缩短,所得到的程序更紧凑,从风格上讲,程序更易阅读。读者将会不断地看到这种风格。(然而,如果再往前走,所编写出的程序可能很难理解,我们将对这种趋势进行遏制。)在w h i l e条件中用于括住赋值表达式的圆括号不能省略。
不等运算符 !=的优先级要比赋值运算符=的优先级高,这就是说,在不使用圆括号时关系测试 ! =将在赋值=之前执行。故语句c = getchar ( )!= EOF等价于c = ( getchar ( ) != EOF )这个语句的作用是把 c的值置为 0或1(取决于g e t c h a r函数在调用执行时所读的数据是否为文件结束标记),这并不是我们所希望的结果。