与前两章一样,以一个简单的程序开始。
// talkback.c -- 演示与用户交互
#include
#include // 提供strlen()函数的原型
#define DENSITY 62.4 // 人体密度(单位:磅/立方英尺)
int main(void){
float weight, volume;
int size, letters;
char name[40]; // name是一个可容纳40个字符的数组
printf("Hi! What's your first name?\n");
scanf("%s",name);
printf("%s, what's your weight in pounds?\n", name);
scanf("%f", &weight);
size = sizeof name;
letters = strlen(name);
volume = weight / DENSITY;
printf("Well, %s, your volume is %2.2f cubic feet.\n", name, volume);
printf("Also, your first name has %d letters,\n", letters);
printf("and we have %d bytes to store it.\n", size);
return 0;
}
运行结果如下:
Hi! What's your first name?
Christine
Christine, what's your weight in pounds?
154
Well, Christine, your volume is 2.47 cubic feet.
Also, your first name has 9 letters,
and we have 40 bytes to store it.
该程序包含以下新特性:
%s
转换说明来处理字符串的输入和输出。注意,在 scanf()
中,name没有 & 前缀,而weight有。strlen()
获取字符串的长度。字符串(character string)是一个或多个字符的序列。
双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串,正如单引号用于标识单个字符一样。
C语言没有专门存储字符串的变量类型,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的存储单元中,每个单元储存一个字符。
注意,C语言会用一个空字符(null character)“\0” 结束。这意味着数组的容量必须至少比待存储字符串中的字符数多1。4.1中有40个存储单元的字符串,智能储存39个字符,剩下一个字符留给空字符。
/* parisel.c -- 使用不同类型的字符串 */
#include
#define PRAISE "You are an extraordinary being."
int main(void){
char name[40];
printf("What's your name? ");
scanf("%s", name);
printf("Hello, %s. %s\n", name, PRAISE);
}
%s
告诉 printf()
打印一个字符串。%s
出现了两次,因为程序要打印两个字符串;一个储存在name数组中;一个有PRAISE来表示。运行结果如下:
What's your name? Angela Plains
Hello, Angela. You are an extraordinary being.
你不用亲自包空字符放入字符串尾,scanf()
在读取输入时就已经完成这项工作。也不用字符串常量PRAISE末尾添加空字符。
注意,scanf()
只读取了 Angela Plains中的Angela,在遇到第一个空白(空格、制表符或换行符)时就不再读取输入。因此,scanf()
在读到Angela和Plains之间的空格时就停止了。换言之,scanf()
只会读取字符串中的一个单词。
上一章提到了 sizeof
运算符,它以字节为单位给出对象的大小。strlen()
函数给出字符串中的字符长度。因为1字节储存一个字符,读者可能认为把两种方法应用于字符串得到的结果相同,但事实并非如此。
// 如果编译器不识别 %zd,尝试换成 %u 或 %lu。
#include
#include
#define PRAISE "You are an extraordinary being."
int main(void){
char name[40];
printf("What's your name? ");
scanf("%s", name);
printf("Hello, %s. %s\n", name, PRAISE);
printf("Your name of %u letters occupies %u memory cells.\n", strlen(name), sizeof name);
printf("The phrase of praise has %u letters ",strlen(PRAISE));
printf("and occupies %u memory cells.\n", sizeof PRAISE);
}
注意,程序使用了两种方法处理很长的 printf()
。第一种方法是将 printf()
语句分为两行(可以在参数之间断为两行,但是不要在双引号的字符串中间断开);第二种方法是使用两个 printf()
打印一行内容,只在第二条 printf()
语句中使用换行符( \n
)。运行结果:
What's your name? Serendipity Chance
Hello, Serendipity. You are an extraordinary being.
Your name of 11 letters occupies 40 memory cells.
The phrase of praise has 31 letters and occupies 32 memory cells.
sizeof
运算符报告,name数组有40个存储单元。但是,只有前11个单元用来储存Serendipity,所有 strlen()
得出的结果是11。name数组的第12个单元储存空字符,strlen()
并未将其计入。
对于PRAISE,用 strlen()
得出的也是字符串中的字符数。然而,sizeof
运算符给出的数更大,因为它把字符串末尾不可见的空字符也计算在内。该程序并未明确告诉计算机要给字符串预留多少空间,所以它必须计算双引号内的字符数。
常量3.14159代表注明的常量 pi(π)。输入实际值便可以使用这个常量。然而,这种情况使用 符号常量(symbolic constant) 会更好。
编译程序时,符号常量会被替换为具体的值。这一过程被称为 **编译时替换(compile-time substitution)。
C90标准新增了 const 关键字,用于限定一个变量为只读。
在运行程序时,程序中所有的替换均已完成。通常,这样定义的常量也称为 明示常量(manifest constant)。
printf() 和 scanf()函数能让用户可以与程序交流,他们是 输入/输出函数,或简称为 I/O函数。
请求printf()函数打印数据的指令要与待打印数据的类型相匹配。例如,打印整数时使用%d,打印字符时使用%c。这些符号被称为 转换说明(conversion specification)。
1、printf()
%*d:*修饰符表示的是字段的宽度。
2、scanf()
%*d:*修饰符表示的是条多相应的输出项。
1、char类型表示单个字符,用字符串表示字符序列。字符常量是一种字符串形式,即用双引号把字符括起来:“Good luck, my friend”。可以把字符串储存在字符数组中。字符串,无论表示成字符常量还是储存在字符数组中,都以一个叫做 空字符 的隐藏字符结尾。
2、在程序中,最好用#define定义数值常量,用const关键字声明的标量为只读变量。程序中使用符号常量(明示常量),提高了程序的可读性和可维护性。
3、C语言的标准输入函数(scanf())和标准输出函数(printf())都使用一种系统。在该系统中,确保转换说明的数量和类型与函数的其余参数相匹配,否则会产生奇怪的结果。必须格外小心。
4、空白字符(制表符、空格和换行符)在scanf()处理输入时起着至关重要的作用。除了%c模式(读取下一个字符),scanf()在读取输入时会跳过非空白字符前的所有空白字符,然后一直读取字符,直至遇到空白字符或正在读取字符不匹配的字符。
字符串是一系列被视为一个处理单元的字符。在C语言中,字符串是以空字符(ASCII码是0)结尾的一系列字符。可以把字符串储存在字符数组中。数组是一系列同类型的项或元素。
要确保有足够多的元素来储存整个字符串(包含空字符)。
字符串常量是用双引号括起来的符号序列。
scanf()函数(声明在string.h头文件中)可用于获得字符串的长度(末尾的空字符不计算在内)。scanf()函数中的转换说明是%s时,可读取一个单词。
C预处理器为预处理器指令(以#符号开始)查找源代码程序,并在开始编译程序之前处理它们。处理器根据#include指令把另一个文件中的内容添加到该指令所在的位置。#define指令可以创建明示常量(符号常量),即代表常量的符号。另外,还可以使用const限定符创建定义后就不能修改的变量。
printf() 和 scanf()函数对输入和输出提供更多种支持。两个函数都使用格式字符串,其中包含的转换说明表明待读取或待打印数据项的数量和类型。另外,可以使用转换说明控制输出的外观:字段宽度、小数位和字段内的布局。
1、再次运行下面程序,但是在要求输入名时,请输入名和姓(根据英文书写习惯,名和姓中间有一个空格),看看会发生什么情况?为什么?
// talkback.c -- 演示与用户交互
#include
#include // 提供strlen()函数
#define DENSITY 62.4 // 人体密度(单位:磅/立方英尺)
int main(void)
{
float weight, volume;
int size, letters;
char name[40]; // name是一个可容纳40个字符的数组
printf("Hi! What's your first name?\n");
scanf("%s",name);
printf("%s, what's your weight in pounds?\n", name);
scanf("%f", &weight);
size = sizeof(name);
letters = strlen(name);
volume = weight / DENSITY;
printf("Well, %s, you volume is %2.2f cubic feet.\n", name, volume);
printf("Also, your first name has %d letters,\n", letters);
printf("and we have %d bytes to store it.\n", size);
return 0;
}
答:程序不能正常运行。第一个scanf()语句只读取用户输入的名,而用户输入的姓仍留在输入缓冲区中(缓冲区是用于储存输入的临时存储区)。下一条scanf()语句在输入缓冲区查找重量时,从上次读入结束的地方开始读取。这样就把留在缓冲区的姓作为体重来读取,导致scanf()读取失败。另一方面,如果在要求输入姓名时输入Lasha 144,那么程序会把144作为用户的体重。
2、假设下列实例都是完整程序中的一部分,它们打印的结果分别是什么?
a. printf(“He sold the painting for $%2.2f.\n”, 2.345e2);
b. printf("%c%c%c\n", ‘H’, 105, ‘\41’);
c. #define Q “His Hamlet was funny without being vulgar.”
printf("%s\nhas %d characters.\n", Q, strlen(Q));
d. printf(“Is %2.2e the same as %2.2f?\n”, 1201.1, 1201.0);
答:a、He sold the painting for $234.50.
b、Hi!(注意,第一个字符时字符常量;第二个字符由十进制整数转换而来;第三个字符时八进制字符常量的ASCII表示)
c、His Hamlet was funny without being vulgar.
has 42 characters.
d、Is1.20e+003 the same as 1201.00?
3、在第二题的c中,要输出包含双引号的字符串Q,应如何修改?
答:在这条语句中使用" :printf(""%s"\nhas %d characters.\n", Q, strlen(Q));
4、找出夏明程序中的错误。
改成:#include<stdio.h> /* 别忘了要包含合适的头文件 */
define B booboo 改成:#define B "booboo" /* 添加#、双引号 */
define X 10 #define X 10/* 添加# */
main(int) 改成:int main(void) /* 不是main(int) */
{
int age;
改成:int xp; /* 声明所有的变量 */
char name[40]; 改成:char name[40];/* 把name声明为数组 */
printf("Please enter your first name."); 改成:printf("Please enter your first name.\n");/* 添加\n,提高可读性 */
scanf("%s", name);
printf("All right, %c, what's your age?\n", name); 改成:printf("All right, %s, what's your age?\n", name);/* %s用于打印字符串 */
scanf("%f", age); 改成:scanf("%d", &age);/* 把%f改成%d,把age改成&age */
xp = age + X;
printf("That's a %s! You must be at least %d.\n", B, xp);
retun 0; 改成:return 0;/* 不是retun */
}
5、假设一个程序的开头是这样:
#define BOOK "War and Peace"
int main(void)
{
float cost = 12.99;
float percent = 80.0;
请构造一个使用BOOK、cost和Percent的printf()语句,打印一下内容:
This copy of "War and Peace" sells for $12.99.
That is 80% of list.
}
答案:记住,要打印%必须用%%:
printf(“This copy of “%s” sells for $%0.2f.\n”, BOOK, cost);
printf(“That is %0.0f%% of list.\n”, percent);
6、打印下列各项内容要分别使用什么转换说明?
a. 一个字段宽度与位数相同的十进制整数
b. 一个形如8A、字段宽度为4的十六进制整数
c. 一个形如232.346、字段宽度为10的浮点数
d. 一个形如2.33e+002、字段宽度为12的浮点数
e. 一个字段宽度为30、左对齐的字符串
答案:a、%d;b、%4X;c、10.3%f;d、%12.2e;e、%-30s
7、打印下面各项内容分别使用什么转换说明?
a. 字段宽度为15的unsigned long类型的整数
b. 一个形如0x8a、字段宽度为4的十六进制整数
c. 一个形如2.33E+02、字段宽度为12、左对齐的浮点数
d. 一个形如+232.346、字段宽度为10的浮点数
e. 一个字段宽度为8的字符串的前8个字符
答案:a、%15lu;b、%#4x;c、%-12.2E;d、%+10.3f;e、%8.8s
8、打印下面各项内容要分别使用什么转换说明?
a. 一个字段宽度为6、最少有4位数字的十进制整数
b. 一个在参数列表中给定字段宽度的八进制整数
c. 一个字段宽度为2的字符
d. 一个形如+3.13、字段宽度等于数字中字符数的浮点数
e. 一个字段宽度为7、左对齐字符串中的前5个字符
答案:a、%6.4d;b、%*o;c、%2c;d、%+0.2f;e、%-7.5s
9、分别写出读取下列各输入行的scanf()语句,并声明语句中用到变量和数组
a. 101
b. 22.32 8.34E-09
c. linguini
d. catch 22
e. catch 22(但是跳过catch)
答案:a、int dalmations; scanf("%d", &dalmations);
b、float kgs, share; scanf("%f%f", &kgs, &share);
(注意:对于本题的输入,可以使用转换字符e、f和g。另外,除了%c之外,在%和转换字符之间加空格不会影响最终的结果)
c、char pasta[20]; scanf("%s", pasta);
d、char antion[20]; int value; scanf("%s %d", action, &value);
e、int value; scanf("%*s %d",&value);
10、什么是空白?
答案:空白包括空格、制表符和换行符、C语言使用空白分隔记号。scanf()使用空白分隔连续的输入项。
11、下面的语句有什么问题?如何修正?
printf("The double type is %z bytes..\n, sizeof(double));
答案:%z中的z是修饰符,不是转换字符,所以要在修饰符后面加上一个它修饰的转换字符。可以使用%zd打印十进制数,或用不同的说明符打印不同进制的数,例如,%zx打印十六进制的数。
12、假设要在程序中用圆括号代替花括号,以下方法是否可行?
#define ( {
#define ) }
答案:可以分别把(和)替换成{和}。但是预处理器无法区分哪些圆括号替换成花括号,哪些圆括号不能替换成花括号。因此,
#define ( {
#define ) }
int main(void)
{
printf("Hello, O Great One!\n");
}
将变成:
int main (void)
{
printf{"Hello, O Great One!\n"};
}
1、编写一个程序,提示用户输入名和姓,然后以“名,姓”的格式打印出来。
#include
int main(void) {
char name[40];
char lastName[40];
printf("What's your name?\n");
scanf("%s", name);
printf("%s, what's your last name?\n", name);
scanf("%s", lastName);
printf("%s,%s\n", name, lastName);
return 0;
}
2、编写一个程序,提示用户输入名和姓,并执行以下操作:
a. 打印名和姓,包括双引号;
b. 在宽度为20的字段右端打印名和姓,包括双引号;
c. 在宽度为20的字段左端打印名和姓,包括双引号;
d. 在比姓名宽度宽3的字段中打印名和姓。
#include
int main(void) {
char name[40];
char lastName[40];
printf("What's your name?\n");
scanf("%s", name);
printf("%s, what's your last name?\n", name);
scanf("%s", lastName);
// 条件a
printf("\"%s\"\n", name);
printf("\"%s\"\n", lastName);
// 条件b
printf("\"%20s\"\n", name);
printf("\"%20s\"\n", lastName);
// 条件c
printf("\"%-20s\"\n", name);
printf("\"%-20s\"\n", lastName);
// 条件d
printf("\"%23s,%-23s\"\n", name, lastName);
return 0;
}
3、编写一个程序,读取一个浮点数,首先以小数点记数法打印,然后以指数记数法打印。用下面的格式进行输出(系统不同,指数计数法显示的位数可能不同);
a. 输入21.3或2.1e+001;
b. 输入+21.290或2.129E+001;
#include
int main(void) {
const float RENT1 = 21.3;
const float RENT2 = +21.290;
printf("%0.1e\n", RENT1);
printf("%0.3E\n",RENT2);
return 0;
}
4、编写一个程序,提示用户输入身高(单位:英寸)和姓名,然后以下面的格式显示用户刚输入的信息:
Dabney, you are 6.208 feet tall
使用float类型,并用/作为除号。如果你愿意,可以要求用户以厘米为单位输入身高,并以米为单位显示出来。
#include
int main(void) {
char name[40];
float height;
printf("What's your name?\n");
scanf("%s", name);
printf("%s, what's you height?\n", name);
scanf("%f", &height);
printf("%s, you are %0.3f feet tall.\n", name, height);
return 0;
}
5、编写一个程序,提示用户输入以兆位每秒(Mb/s)为单位的下载速度和以兆字节(MB)为单位的文件大小。程序中应计算文件的下载时间。注意,这里1字节等于8位。使用float类型,并用/作为除号。该程序要以下面的格式打印3个变量的值(下载速度、文件大小和下载时间),显示小数点后面两位数字:
At 18.12 megabits per second, a file of 2.20 megabytes downloads in 0.97 seconds.
#include
int main(void) {
float speed;
float size;
printf("Please tell me your speed(Mb/s)\n");
scanf("%f", &speed);
printf("Please tell me your file size(MB)\n");
scanf("%f", &size);
printf("At %0.2f megabits per second, a file of %0.2f megabytes downloads in %0.2f seconds\n", speed, size, size * 8 / speed);
return 0;
}
6、编写一个程序,先提示用户输入名,然后提示用户输入姓。在一行打印用户输入的名和姓,下一行分别打印名和姓的字母数。字母数要与相应名和姓的结尾对齐,如下所示:
Melissa Honeybee
7 8
#include
#include
int main(void) {
char name[7];
char lastName[8];
printf("What's your name?\n");
scanf("%s", name);
printf("%s, what's your last name?\n", name);
scanf("%s", lastName);
printf("%s %s\n", name, lastName);
printf("%7d %8d\n",strlen(name), strlen(lastName));
return 0;
}
7、编写一个程序,将一个double类型的变量设置为1.0/3.0,一个float类型的变量设置为1.0/3.0。分别显示两次计算的结果各3次;一次显示小数点后面6位数字;一次显示小数点后面12位数字;一次显示小数点后面16位数字。程序中要包含float.h头文件,并显示FLT_DIG和DBL_DIG的值。1.0/3.0的值与这些值一致吗?
#include
#include
int main(void) {
double rentD = 1.0 / 3.0;
float rentF = 1.0 / 3.0;
printf("rentD = %0.6f\n", rentD);
printf("rentF = %0.6f\n", rentF);
printf("rentD = %0.12f\n", rentD);
printf("rentF = %0.12f\n", rentF);
printf("rentD = %0.16f\n", rentD);
printf("rentF = %0.16f\n", rentF);
printf("FLT_DIG = %d\n", FLT_DIG);
printf("DBL_DIG = %d\n", DBL_DIG);
return 0;
}
8、编写一个程序,提示用户输入旅行的里程和消耗的汽油量。然后计算并显示消耗每加仑汽油行驶的英里数,显示小数点后面以为数字。接下来,使用1加仑大约3.785升,1英里大约为1.609千米,把单位是英里/加仑的值转换为升/100公里(欧洲通用的燃料消耗表示法),并显示结果,显示小数点后面1位数字。注意,美国采用的方法测量消耗单位燃料的行程(值越大越好),而欧洲采用单位距离消耗的燃料测量方案(值越低越好)。使用#define创建符号常量或使用const限定符创建变量来标识两个转换系数。
#include
#define TRANS_GASO 3.785
#define TRANS_MILE 1.609
int main(void) {
float mileage,gasoline;
printf("请输入旅行的里程(单位:英里)!\n");
scanf("%f", &mileage);
printf("请输入消耗的汽油量(单位:加仑)!\n");
scanf("%f", &gasoline);
printf("%0.1f 英里/加仑\n", mileage / gasoline);
float gaso = gasoline * TRANS_GASO;
float mile = mileage * TRANS_MILE / 100;
printf("%0.1f 升/100公里\n", gaso / mile);
return 0;
}