
4.6 格式数据的输入/输出
本节主要介绍格式输出函数printf与格式输入函数scanf。
4.6.1 格式输出函数printf
printf函数称为格式输出函数,其关键字最末一个字母f即为“格式”(format)之意。其功能是按用户指定的格式,把指定的数据显示到显示器屏幕上。在前面的例题中已多次使用过这个函数。
1.printf函数调用的一般形式
printf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。但作为一个特例,不要求在使用printf函数之前必须包含stdio.h文件。
函数原型:_Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_const char*_Format, …);
其中,const char*是printf函数的第一个参数,显然是指向“只读”字符串的字符指针。…表示不定长参数,即函数的参数个数是不确定的。__cdecl编译规则支持具有不定参数的函数;有了__cdecl编译规则,在使用printf函数时才可以第2个参数开始的参数数目可变。
printf函数调用的一般形式为

其中,格式控制字符串用于指定输出格式,往往是双引号括起来的字符串常量。格式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度和小数位数等。
● "%d"表示按十进制整型输出。
● "%ld"表示按十进制长整型输出。
● "%c"表示按字符型输出等。
非格式字符串在输出时原样照印,在显示中起提示作用。注意:非格式字符串中含有转义字符时,将输出转义后的字符,例如,"\115oon"将输出Moon,"\n"将输出换行符而产生换行功能。
输出表列中给出了各个输出项,要求格式字符串和各输出项在数量和类型上应该一一对应匹配。
【例4-5】多格式输出两整型变量的值及其对应的字符。

运行结果:
本例中4次输出了a,b的值,但由于格式控制串不同,输出的结果也不相同。第3行的输出语句格式控制串中,两格式串%d之间加了一个空格(非格式字符),所以输出的a,b值之间有一个空格。第4行的printf语句格式控制串中加入的是非格式字符逗号,因此输出的a,b值之间加了一个逗号。第5行的格式串要求按字符型输出a,b值。第6行中为了有提示,输出结果又增加了非格式字符串。
注意:要输出普通%字符,可用2个%(即%%)在格式控制字符串中来表示。
2.格式字符串
格式字符串的一般形式为

其中,方括号[]中的项为可选项,各项的意义介绍如下。
1)类型:类型字符用以表示输出数据的类型,其类型格式符和含义等如表4-1所示。
表4-1 printf格式字符串中类型、含义及应用举例

注:表中变量int i=123;char c=′A′;float f=3.1415;double d=1234.567890;
2)标志:标志字符为-、+、#、空格4种,其含义等如表4-2所示。
表4-2 printf格式字符串中标志、含义及使用形式

3)输出最小宽度(m):用十进制整数来表示输出的最少位数(包括正负号、小数点)。若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格,如"%5d%10f%15e"。
4).精度(n):精度格式符以“.”开头,后跟十进制整数。本项的意义是:如果输出数字,则表示小数的位数;如果输出的是字符,则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分(对小数位数四舍五入截去),如"%10.3f%15.4e%20.5g"。
5)长度:长度格式符为h,l两种,h表示按短整型量输出,l表示按长整型量输出,l在e,f,g前,指定输出精度为double。如"%hd,%ho,%hx,%hu,%hi,%ld,%lo,%lx,%lu,%li,%lf,%le,%lg\n"。
【例4-6】 printf函数中格式控制符集中示例。


运行结果:
说明:请读者逐行对照或使用格式符时查阅,格式符的使用不需要死记硬背,有所了解就行,贵在实践中了解格式符的使用功效。
注意:一个程序的运算结果是否正确?是否有误差?要问答这些问题,至少要能认识到两个方面的内容。一方面计算机的实数运算是非精确运算,是有误差的,整数运算也要注意是否会溢出而产生差错;另一方面程序运行结果,通过printf格式化输出时,又有转换处理格式输出的问题,可能会因为有效数位的限制、格式符使用不恰当等而使显示结果有误差或不正确(变量等内存中的值并非显示结果那样)。因此,今后调试程序发现意外结果时,格式符的使用是否得当也该重点检查。
【例4-7】 printf函数中格式控制符集中示例(二)。


运行结果:
说明:本例第5行中以4种格式输出整型变量a的值,其中“%5d”要求输出宽度为5,而a值为15只有两位,故补3个空格。第6行中以4种格式输出实型量b的值。其中“%f”和“%lf”格式的输出相同,说明“l”对“f”类型无影响。“%5.4lf”指定输出宽度为5,精度为4,由于实际长度超过5故应该按实际位数输出,小数位数超过4位部分被四舍五入方式截去。第7行输出双精度实数,“%8.4lf”由于指定精度为4位故被四舍五入截去了超过4位的部分。第8行输出字符量d,其中“%8c”指定输出宽度为8,故在输出字符p之前补加7个空格。
【参考知识】
使用printf函数时还要注意一个问题,那就是输出表列中的求值顺序。不同的编译系统不一定相同,可以从左到右,也可从右到左。VC++ 6.0/2010、Win-TC都是按从右到左进行的,但从运行结果来看,它们的运行机理也是不尽相同的。具体请扫二维码参阅。
C语言的学习就是要在实践中去深入探究与领会。
扩展阅读:函数参数运行顺序的处理情况探究

4.6.2 格式输入函数scanf
scanf函数称为格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。
1.scanf函数的一般形式
scanf函数是一个标准库函数,它的函数原型也在头文件“stdio.h”中,与printf函数相同,C语言也允许在使用scanf函数之前不必包含stdio.h文件。
函数原型:_CRTIMP int__cdecl scanf(_In_z__Scanf_format_string_const char*_Format, ...);
scanf函数的一般形式为

其中,格式控制字符串的作用与printf函数相同,但输入时不能显示非格式字符串,也就是不能显示提示字符串。地址表列中给出各变量的地址。地址是由地址运算符“&”后跟变量名或数组元素组成的。
例如,&a,&b分别表示变量a和变量b的地址。这个地址就是编译系统在内存中给a,b变量分配的地址。在C语言中使用了地址的概念,这是与其他语言的不同之处。应该把变量的值和变量的地址这两个不同的概念区别开来。变量的地址是C编译系统分配的,用户不必关心具体的地址是多少。
变量的地址和变量值的关系如下:
在赋值表达式中给变量赋值,如a=5678,则a为变量名,5678是变量的值,&a是变量a的地址(具体地址值不重要)。
但在赋值号左边是变量名,不能写地址,而scanf函数在本质上也是给变量赋值,但要求写变量的地址,如&a。这两者在形式上是不同的。&是一个取地址运算符,&a是一个表达式,其功能是求变量的地址。
【例4-8】通过scanf输入3个整型变量。

说明:在本例中,由于scanf函数本身不能显示提示串,故先用printf语句在屏幕上输出提示,意为请用户输入a、b、c的值。执行scanf语句时,在屏幕上则等待用户输入。用户输入“7 8 9”后按〈Enter〉键,此时,系统将打印出刚读到的a,b,c的值。在scanf语句的格式串中由于没有非格式字符在“%d%d%d”之间作输入时的间隔,因此,在输入时要用一个或一个以上的空格键或〈Enter〉键作为每两个输入数之间的间隔。

2.格式字符串
格式字符串的一般形式为

其中,方括号[]项为任选项,各项的意义如下。
1)类型:表示输入数据的类型,其格式符和含义如表4-3所示。
表4-3 scanf格式字符串中类型格式符、含义及应用示例

【例4-9】 scanf函数中格式控制符集中示例(对多种类型变量等的输入与输出)。

运行结果1:
运行结果2:
说明:请试着输入不同数据或不同输入数据间隔,来体会格式输入是如何进行的。为何字符变量c与数组a输入时要紧挨着一起输入“aChina”?是否可以有其他输入方式来让c读取到“a”,a数组读取到“China”呢?
2)“*”符:用以表示该输入项,读入后不赋予相应的变量,即跳过该输入值。如“scanf("%d%*d%d",&a,&b);”。当输入1 2 3时,把1赋予a,2被跳过,3赋予b。
3)输入数据宽度:用十进制整数指定输入的宽度(即字符数)。
例如,“scanf("%5d",&a);”,则输入12345678只把12345赋予变量a,其余部分被截去。
又如,“scanf("%4d%4d",&a,&b);”,则输入12345678将把1234赋予a,而把5678赋予b。
4 )长度:长度格式符为l和h ,l表示输入长整型数据(如%ld)和双精度浮点数(如%lf),h表示输入短整型数据。
使用scanf函数还必须注意以下几点。
1)scanf函数中没有精度控制,如“scanf("%5.2f",&a);”是不合法的(输入时不能正确获取实数)。不能用此语句输入小数位数为2位的实数。而“scanf("%5f",&a);”是合法的,其中的“5”为输入数据宽度。
2)scanf中要求给出变量地址,如给出变量名则会出错。例如,“scanf("%d",a);”是不合法的,应改为“scanf("%d",&a);”才是合法的。
3)在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格、〈TAB〉键或〈Enter〉键作间隔。
输入数据时,遇以下情况时认为该数据输入已结束:遇空格、〈Enter〉键、〈TAB〉键等;满宽度(如%3d,则满3位数字即输入结束);遇非法字符输入。
4)在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
例如,“scanf("%c%c%c",&a,&b,&c);”,若输入d e f则把′d′赋予a,′′赋予b,′e′赋予c。只有当输入为def时,才能把′d′赋予a,′e′赋予b,′f′赋予c。
5)如果在格式控制中加入空格作为间隔,如“scanf ("%c%c%c",&a,&b,&c);”,则输入时各数据之间可以加若干空格类分隔符。
说明:空格类分隔符,是指空格、〈Tab〉键、〈Enter〉键等。
【例4-10】 scanf输入两个字符到两字符变量示例(一)。

由于scanf函数"%c%c"中没有空格,输入M N,结果输出只有M。而输入改为MN时则可输出MN两字符。
【例4-11】 scanf输入两个字符到两字符变量示例(二)。

本例表示scanf格式控制串"%c %c"之间有空格时,输入的数据之间可以有若干空格等分隔符间隔。
6)如果格式控制串中有非格式字符则输入时也要原样输入该非格式字符。
例如,“scanf("%d,%d,%d",&a,&b,&c);”,则用非格式符“,”作间隔符,故输入时应为:5,6,7。
而语句“scanf("a=%d,b=%d,c=%d",&a,&b,&c);”,则输入应为:a=5,b=6,c=7。
如在Win-TC中,输入的数据与输出的类型不一致时,虽然编译能够通过,但结果将不正确。如果输入数据类型为整型,而输出语句的格式串中说明为长整型,因此输出结果和输入数据不符。
【例4-12】输入的数据与输出的类型不一致的情况。

在Win-TC中的运行结果:,即当输入12345时输出809054265了。
说明:本程序在Win-TC中运行有异常,输入与输出不一致。在VC++ 2010中,虽能正确运行,但还是建议变量类型与格式串中说明类型要保持一致,这是始终能保持正确的做法。
【例4-13】输入的数据与输出的类型一致。

运行结果:
当输入数据改为长整型后,输入与输出数据完全一致。
【例4-14】输出各类型变量的字节数

运行结果:在VC++2010中运行结果:;
在Win-TC中运行结果: