程序2-1和程序2-2都使用了格式化输出函数printf()来输出运算的结果,此函数位于标准C语言库中,其函数原型在头文件stdio.h中,所以在程序开头要加上文件包含指令:
#include<stdio.h>
pirntf()函数的标准格式为:
printf("格式控制串",输出参数表);
格式控制串是指定数据的输出格式,格式控制串由格式字符(包括转换说明符、标志、域宽、精度)和普通字符组成。转换说明符和百分号(%)一起使用,用来说明内存中数据的输出格式。例如说明符%d指定printf()函数把int型数值从二进制形式转换成十进制整数,转换说明符%f是对float型数值进行类似的转换。普通字符在输出时原样输出(在显示时可起到提示作用)。
输出参数表是指待输出的数据,可以是常量、变量或其他更复杂的表达式,也可以没有输出项。当有多个输出项时,各输出项之间用逗号隔开。输出项必须与格式字符在类型和数量上完全对应。
1.输出格式中的普通字符
当printf()函数的参数只有“格式控制串”,且“格式控制串”中没有格式字符而只有普通字符时,函数完成的功能是将双引号中的字符串原样输出(显示在屏幕上)。例如:
printf("hello C !");
此句是将“hello C !”在显示器屏幕上原样输出。
2.输出格式中的转换字符串
转换说明符规定了对应输出项的输出类型,即将输出的数据转换为指定的格式输出。该项不能省略。格式为:
%转换说明符
常用的转换字符串及其说明如表2-2所示。
表2-2 常用的转换字符串
例如:
int visitorCount=150;
printf("%d",visitorCount);
输出结果为:150。
3.输出格式中的宽度修饰符
宽度修饰符用一个十进制整数来表示输出数据的位数,插在%与转换说明符之间,其作用是控制打印数据的宽度,也称为“域宽”。
·%md代表数据以十进制整数形式输出,宽度占m列,数据不足m列的左补空格,超过m列的情况下按实际位数输出。例如:
int salary=5500;
printf("%10d",salary);
输出结果为: 5500。
输出结果中数值占4位,靠右对齐,左边有6个空格。
·%-md代表数据以十进制整数形式输出,宽度占m列,数据不足m列的右补空格,超过m列的情况下按实际位数输出。例如:
int salary=5500;
printf("%-10d",salary);
输出结果为:5500 。
输出结果中数值占4位,左对齐,右边有6个空格。
解决问题:输出整数幂
输入两个正整数m和n,分行输出m和n的值及其2次方和3次方,要求每个数据占8列,左对齐。例如,若输入: 11 41 ,则输出如下:
11 121 1331
41 1681 68921
解题思路:
(1)m的2次方可以使用表达式m*m计算,m的3次方可以用表达式m*m*m计算,尽管有计算幂的库函数pow,但因为其计算结果不是int类型,此处不建议使用。
(2)题目要求“每个数据占8列,左对齐”,可使用格式控制字符串“%-8d”:
printf("%-8d%-8d%-8d\n",m,m*m,m*m*m);
注意,若要严格控制每个数据占8列,则每个“%-8d”后面不要再随意加入空格,否则列宽就多于8列了。
/*程序2-3:输出整数幂*/
从键盘读取数据的最灵活的方法是使用库函数scanf()。scanf()函数从标准输入(键盘)读取信息,按照格式描述把读入的信息转换为指定数据类型的数据,并把这些数据存入指定的内存区域中。例如语句:
scanf("%d",&fahr);
从键盘读取一个十进制整数,并将其赋给int型变量fahr。%d为格式转换符,每个格式转换符对应后面的一个内存地址。scanf()从标准输入流按指定格式读取数据,然后转换为机内码存入对应内存地址。变量名前的"&"是地址运算符,作用是获取变量的地址。
scanf()函数的格式:
scanf(格式控制字符串,内存地址1,内存地址2,…,内存地址n)
整型变量有6种不同类型,整型常量有不同进制的书写方法,而%d仅适合于为int类型变量读入十进制表示的数据。为了读写无符号整数、短整数、长整数以及十进制和八进制数,需要一些新的转换说明符。
(1)格式符d、i、u、o、x:
%d或%i以十进制有符号整数形式转换输入数据;
%u以十进制无符号整数形式转换输入数据;
%o以八进制有符号整数形式转换输入数据;
%x或%X以十六进制有符号整数形式转换输入数据。
(2)h或l前缀:
读入短整数时,加前缀字母h,如%hd;
读入长整数时,加前缀字母l,如%ld;
(3)读入64位整数:
如果编译器支持__int64,用格式符%I64d,如果编译器支持long long,用格式符%lld。
(4)宽度:
用十进制整数指定输入的宽度(即字符数)。例如:
scanf("%5d",&a);
输入:12345678
只把12345赋予变量a,其余部分被截去。
(5)赋值抑制符号*:
用以表示该输入项读入后不赋予相应的变量,即跳过该输入值。例如:
scanf("%d %*d %d",&a,&b);
当输入为:1 2 3时,把1赋予a,2被跳过,3赋予b。
很多时候程序要从已有的文件中读入数据,文件只能顺序读取,经常需要在读入时跳过某些不需要的信息。例如读入某人的18位身份证号,输出其出生日期可用语句:
scanf("%*6d%4d%2d%2d%*d",&year,&month,&day);
printf("%4d-%2d-%2d",year,month,day);
这段代码将18位的身份证号分为几个部分读入:用%*6d将从缓冲区读入6个数字,但不存储,接着由%4d读入4个数字存入year,然后读入两位数字存入month,再读入两位数字存入day,最后用%*d将其余数字从缓冲区读入,但不存储。若输入:
410102197805210123
则输出:
1978-05-21
程序2-1的运行结果并不令人满意,因为华氏温度100对应的摄氏温度应该为37.777…而不应该是37。int类型不适用于所有应用,这里需要用浮点型变量来存储带小数点的数,需要将celsius和fahr声明为浮点类型。
试试看:从键盘上输入任意一个浮点数作为华氏温度,编程输出相对应的摄氏温度。
/*程序2-4:对任意浮点数进行温度转换*/
程序2-4的语句
double celsius,fahr;
是将celsius和fahr声明为双精度浮点变量,而在语句
printf("fahr=%f,celsius=%f\n",fahr,celsius);
中printf的格式转换符%f用来输出浮点数据。语句
scanf("%lf",&fahr);
中scanf的格式转换符%lf用来读入双精度浮点数据。
浮点型变量用来存储浮点数,浮点数既可表示小数,也可表示分数和整数。浮点数在计算机中的表示是基于指数表示法,分为指数和尾数来存储。根据数据表示范围和精确度不同,C语言提供了3种不同的浮点格式float、double和long double。IEEE标准中浮点类型的一些特征如表2-3所示。
表2-3 浮点类型(IEEE标准)
long double类型没有显示在此表中,因为它的长度随着机器的不同而变化,最常见的是80位和128位,而VC++6.0中,long double和double类型的长度相同,都是64位。
1.转换字符串%f
格式转换符f用于浮点数据的输出格式限定,在程序2-3中,语句
printf("fahr=%f,celsius=%f\n",fahr,celsius);
是将浮点型变量fahr和celsius以小数形式输出到屏幕上,输出结果为:fahr=100.000000,celsius=37.777778。两个浮点型变量以小数形式输出时,小数部分精确到6位。
2.转换字符串%m.nf
m表示输出宽度,而n表示精度,指定小数点后显示的数字个数。例如:
double mercuryLevel=168.2251074;
printf("%7.2f",mercuryLevel);
输出结果为:168.23。
此输出函数是将mercuryLevel变量以单精度小数形式输出,要求输出时宽度(所有数字和小数点所占的位数)为7位,右对齐,小数点后精确到2位。
当指定的输出宽度大于数据的实际宽度,则默认在宽度内为右对齐,左补空格或补0,直到满足指定宽度。当指定的输出宽度小于数据的实际宽度时,则按实际数据的位数输出(宽度自动增加)。对于整数而言,按该数的实际宽度输出,对浮点数而言,按实际位数输出,但如果指定浮点数的精度,则相应的小数位按精度的位数四舍五入。
如果没有为要输出的数据提供足够大宽度,可能造成其他输出数据发生位置偏移,从而产生不可预料的输出结果。
在输入浮点数时,输入单精度可以使用格式符f、g、e,输入双精度浮点数需要加前缀l。可以指定读入数据的宽度,但不能指定精度。如:
如下程序段定义一个double类型的变量a,然后为其读入数据,在读入数据时指定读入宽度,最后输出a的值。
double a;
scanf("%5lf",&a);
printf("%f\n",a);
若输入:12.34567,则输出:12.340000。若输入:1.2e12,则输出:12.0000000。因为指定了输入宽度为5,在输入12.34567时,只读取前5个字符12.34,转换为双精度浮点型存入变量a;在输入1.2e12时,只读取前5个字符1.2e1,转换为双精度浮点型存入变量a。
在scanf()函数的格式控制字符串中若有普通字符,则输入时在对应位置也必须输入该普通字符,否则会因读入格式错误而终止程序。
例如:scanf("%d,%d",&a,&b);
格式控制字符串中有普通字符逗号,所以输入时两个数据之间必须输入一个逗号,即输入:3,5。
例如:scanf("a=%d,b=%b",&a,&b);
此时输入数据的格式应该是:a=3,b=5。
scanf的格式控制字符串中最好不加任何普通字符,只写%开头的格式转换符。如scanf("%d%d",&a,&b);
不要给用户制造输入格式限制,以免增加输入操作错误的几率。
和变量一样,常量也是程序使用的一个数据存储位置。和变量不同的是,在程序运行期间,存储在常量中的值是不能修改的。C语言中的常量分为字面常量、宏常量、const常量。
1.字面常量
字面常量是指在源代码中直接输入的值,分为以下4种:
·整型常量,如:32、100、071、0x3A;
·浮点常量,如:1.23、123.567E5;
·字符常量,如:'a'、'2'、'\n';
·字符串常量:如:"c program"。
C语言支持的整型字面量有十进制、八进制和十六进制。八进制常量以前缀“0”开头,如071,为八进制数,对应的十进制数为57。十六进制常量以“0x”开头,如0x3A,为十六进制数,对应十进制数为58。
2.宏常量
宏常量是指为使程序易于阅读和便于修改,给程序中经常使用的常量定义一个有一定含义的名字,常量用于定义具有以下特点的数据:
·在程序中保持不变;
·在程序内部频繁使用;
·需要用比较简单的方式替代某些值;
·防止意外的修改,增强程序的健壮性。
定义宏常量格式:
#define 标识符常量 替换文本
#define编译指令的准确含义是,命令编译器将源代码中所有标识符常量替换为替换文本。其效果与使用编辑器手工进行查找并替换相同。
例如:#define PI 3.1415926
编译预处理指令#define将PI定义成一个要被3.1415926取代的符号,此时PI不是一个变量,而是3.1415926的别名。在编译开始之前,只要在程序的表达式中引用PI,预处理器就会用#define指令中的值3.1415926来取代它。
根据约定,符号常量名中的字母为大写,这易于将其同变量名区分开来。
#define语句不以分号结尾,可以位于源代码的任何位置,它定义的常量只在后面的源代码中有效。
一般情况下,程序员将所有的#define放在一起,并将它们放在程序的开头。
宏常量也有其自身的缺点,就是宏常量被替换成替换文本之后,内存中有同一个替换文本的多份副本。
3.const常量
const常量与变量定义形式类似,加上const修饰,告诉编译器,它的值是固定的,不能被改变,编译器会帮你检查、监督。
定义const常量,用到关键字const。例如:
const double PI=3.1415926;
const推出的初始目的,正是为了取代宏常量,消除其缺点,同时继承其优点。
在编译的时候,由于const定义常量只是给出了对应的内存地址,而不是像#define给出的是替换文本,所以const定义的常量在程序运行过程中只有一份副本,而#define定义的常量在内存中有若干份副本。
下面通过程序2-5来学习如何使用常量。
试试看:定义常量。
已知一个圆柱体的底面半径和高,求圆柱体的体积,圆周率用宏常量来表示。
/*程序2-5:求圆柱体体积*/
也可以使用const声明常量PI将程序2-5中第2行改成const double PI=3.1415926。