用程序解决简单问题一般需要3步:①输入数据;②处理数据;③输出数据。例如,小
程是一家销售瓷砖的网上商铺老板,需要经常根据用户提供的铺设瓷砖的面积和选择瓷砖
的尺寸,计算需要瓷砖的块数。

这是一个小学生就能解决的问题,假设铺设面积用area表示,单位为平方米,瓷砖的长
用legt宽用wdh 
表示
, 
iera×100÷ 
(egt

nh表示
, 
it单位为厘米。则瓷砖块数tls=aelnh× 
width)。编写程序解决这一实际需求可以分为3步。

(1)输入数据。通过键盘输入area、length、width的值。
(2)处理数据。根据上述公式求出瓷砖的块数。
(3)输出数据。将瓷砖块数输出到屏幕
。
确定总体思路后,还需要确定程序的若干细节问题
。
(1)aa等变量的值如何从键盘输入,s中的值如何输出到屏幕上呢
?
retile


62 
(2)根据公式计算出来的瓷砖块数一般带有小数点,如32.6块,而实际需要33块,如何
实现向上取整呢? 
本章主要介绍顺序结构、C语句分类、输入以及常用函数的有关知识,运用本章的知识, 
读者可以编写出顺序结构的人机交互小程序。
3.1 顺序结构 
如果用计算机程序解决某一个问题或完成某一任务,需要若干条语句配合完成,而反映
图3-1 顺序结构流程图
语句之间的这种配合和执行顺序关系的就是程序流程控制问题。
从程序流程控制视角看,程序结构可分为3种基本结构:顺序结
构、选择结构和循环结构。无论功能多复杂的程序,皆是由这3种
基本结构中的一种或几种灵活组合而成。
本章介绍的顺序结构是其中最简单也是最常用的一种结构。
顺序结构是指按照语句在程序中出现的先后顺序逐条执行,每条
语句必须执行且只执行一次。其流程图如图3-1所示,即语句1执
行完毕后再执行语句2。
注意:顺序结构中的某些语句执行顺序影响程序执行结果,不
可以交换。例如,计算瓷砖数的案例中,按(2)→(1)→(3)顺序执行,会得到错误结果,因为
计算tiles的值时area、length、width还没有输入数据,当然tiles无法得到正确结果。但某
些语句交换后不影响程序结果,读者可以根据个人偏好调整,例如,area、length、width3个
变量的赋值顺序可以调整,只要使用时保证已经赋值即可。
3.2 C语句分类 
一个C语言程序由一个或多个函数组成,而函数主要由语句组成。因此函数是C语言
程序的基本组成单位,而语句是最小单位。根据语句的功能,C语言中的语句可以分为以下
4类。
(1)表达式语句。表达式语句的突出特征是以分号“;”结尾。任何一个合法的表达式
后面跟上一个分号就可以构成一条表达式语句,即其一般格式如下: 
表达式; 
表达式由运算符和运算数组合而成,其中运算符可以是C语言中任何的算术、关系、赋
值、逗号、指针等运算符,运算数可以是变量、常量、宏定义、函数调用等形式,只要组成的表
达式合法即可。其中,最典型、最常用的是赋值表达式和函数调用语句。
例如:赋值表达式语句 
x=y+z; 
扫一扫
扫一扫

63 
是由算术运算符“+”和赋值运算符“=”以及x、y、z3个变量组成的算术、赋值混合表达式
语句,其作用为计算y+z 的值,然后赋给变量x。因为最后进行的是赋值操作,可以简称
为赋值表达式语句。
例如:函数调用语句 
printf("Try my best!"); 
是由一个函数调用“printf("Trymybest!")”和一个分号组成,其作用是调用printf函数, 
在屏幕上输出“Trymybest!”。
当然,任何合法的表达式加分号都构成表达式语句,但不一定有实际意义,应避免使用。
例如: 
y+z; //只计算出y +z 的值,但计算结果没有保留,也没有输出,无实际意义
z=z; //把z 变量的值又赋给z ,对程序功能没起任何作用,无实际意义
(2)流程控制语句。在C语言中,除了顺序结构通过出现顺序控制之外,其他流程均需
要通过特定的语句定义符控制。C语言中的流程控制语句共有3大类9小种。
① 选择语句:if语句、switch语句。
② 循环语句:while语句、for语句和do…while语句。
③ 转向语句:break语句、continue语句、return语句、goto语句。
(3)空语句。空语句是一条只有一个分号“;”构成的语句。它其实是一个表达式语句, 
只不过表达式为空,所以什么也不做,一般用来占位、作被转向点或空循环体。例如: 
for(i=0;(a[i]=getchar())!='\n';i++) 
; //注意循环体为空语句
该程序段的功能为从键盘上输入一串字符放到数组中,按回车键结束。因为循环必须
有循环体,因此该空语句必不可少。当然,此处的空语句并不是空循环体的唯一表达方式, 
换成一条空复合语句,即一对花括号“{}”也可以。
虽然什么也不做,但空语句毕竟是一条语句,不能在程序中随便加,否则可能会造成程
序语法或者逻辑错误。例如: 
int count=0; 
while((ch=getchar())!='\n') 
count++; 
此程序段的功能为计算输入字符的个数。但如果写成: 
int count=0; 
while((ch=getchar())!='\n'); 
count++; 
则会发现无论输入多少个字符,count中的值都为1。这是因为while循环的循环体变成
了空语句“;”,而“count++;”变成了循环结束后才能执行到的顺序结构语句,只执行
一次。

64 
(4)复合语句。前面的一条表达式语句、空语句或流程控制语句均可统称为一条单语
句,而将n(n≥0)条语句用一对花括号“{}”括起来则称为复合语句。例如: 
{ 
temp=max; 
max=min; 
min=temp; 
} 
就是一条复合语句,功能为借助中间变量temp交换变量max、min的值。
如果去掉两端的花括号,则是三条单语句,但功能不变。既然花括号加与不加功能不
变,为什么要画蛇添足地加呢? 因为在某些场景中,比如选择或循环语句,要求分支体或者
循环体只能是一条语句,所以,当分支体或者循环体是多条语句时,则可以加花括号将多条
语句转换为一条复合语句。例如: 
if (max<min) 
{ 
temp=max; 
max=min; 
min=temp; 
}
功能为只有在满足max小于min的情况下才会交换max、min的值,也就是始终保持
max大于或等于min。此时若去掉花括号,则受if控制的分支体就只有单语句“temp= 
max;”,程序就会产生逻辑错误。
【例3-1】 编程实现,将一个24小时制时间转换为12小时制时间。例如,输入:13∶15, 
则输出:下午1∶15;输入:8∶20,则输出:上午8∶20。
程序分析:时间由时和分两部分组成,转换为12小时制时只有时变,而分不用变,因此
将时和分放在两个整型变量中。 
#include <stdio.h> //文件包含命令,不是语句,所以没有分号
int main() 
{ 
int hour,minute; //变量声明语句 
printf("输入24 小时制时间:(小时:分钟)\n");//函数调用语句,输出提示信息 
scanf("%d:%d",&hour,&minute); //函数调用语句,从键盘输入时分,用:分隔 
}

65 
3.3 常用数据输出输入函数 
输入与输出是程序与用户之间的一种交互方式,一个程序至少有0个输入和1个输出。
C语言通过调用标准函数库中的输入、输出函数来实现数据的输入、输出操作。因为输入设
备可能是标准输入设备键盘,也可能是非标准输入设备,如文件、扫描仪等,输出设备可能是
标准输出设备屏幕,也可能是非标准输出设备,如文件、打印机等,因此在标准函数库中提供
了针对不同设备、不同读写方式的多个函数,这些函数的定义一般在头文件stdio.h(stdio是
standardinput&output的缩写)中。因此,如果程序中要使用它们,则需要在程序的开头
加上预编译命令: 
#include <stdio.h> 
C语言中的输出输入函数都是成对的,即有一个某功能的输入函数,就有一个相同功能
的输出函数。例如,scanf和printf是一对,都是格式化输入输出函数;getchar和putchar是
一对,都是字符输入输出函数。一对函数在格式、用法等方面有很多雷同点,因此可以对比
着记忆、学习。
此章要介绍的输入函数皆为标准输入函数,即输入设备为键盘;输出函数皆为标准输出
函数,即输出设备为屏幕。输入和输出设备为文件的若干函数将在文件一章中介绍。
3.3.1 格式输出函数printf 
1 . printf 函数调用方法 
格式输出函数printf的功能是按指定格式将字符串输出到标准输出设备———屏幕上。
其中,函数名中的f就是format(格式)的缩写。printf函数是应用最广、功能最强大的输出
函数,因此使用语法也比较琐碎,初学者只需掌握基本输出方式即可,不必死抠细节,尤其是
繁杂的格式说明项,用时查阅即可。
printf函数的函数原型为: 
int printf(const char *format é,argument…. ) 
其中,参数format称为格式控制串,参数argument后面的“…”表示此参数的个数不确定, 
所以argument称为输出列表,“é. ”表示参数argument为可选项,可有可无。
例如: 
扫一扫

66 
(1)格式控制串。format前面的数据类型“constchar*”说明此处应是一个字符型指
针,常为一个用双引号引起来的常量字符串,功能为指定输出数据项的类型和格式,可以包
含两种信息。
① 格式说明项,由%和格式说明符组成,作用是将输出列表中对应的数据按照指定格
式输出。如例中的%d说明按十进制整型格式输出,%c说明按字符格式输出,但具体输出
值需要在输出列表中指定。
② 普通字符,即除格式说明项以外的字符,会原样输出,在输出结果中起提示作用。如
例中的“a=”和“,b=”,尤其注意“,b=”中的“,”也是普通字符。
普通字符一般采用字符常量形式,特殊情况也可以采用转义字符形式,例如: 
printf("a\b*"); 
输出结果为:*。因为“\b”为退格键的转义字符形式,输出a以后会倒退一格,然后输
出*,所以*就会把a覆盖掉。
(2)输出列表。输出列表是需要在格式控制串指定位置输出的一些表达式集合,之间
用逗号隔开。第i 个表达式的值会按照第i 个格式说明项所指定格式,代替第i 个格式说
明项输出。所以表达式的个数一定要和格式说明项的个数保持一致,否则程序会出现意想
不到的逻辑错误。
在上例中,假如变量a、b 均为整型变量,且值均为65,则输出结果为: 
a=65,b=A 
即第一个表达式的值代替第一个格式说明项,以整数形式65输出;第二个表达式的值
代替第二个格式说明项,以字符形式A 输出(字符A 对应的ASCII码为65)。除此之外,其
他均为普通字符,原样输出。
(3)函数返回值。printf函数如果输出成功,则返回所输出字符的个数,否则返回一个
负数。但最常用的是printf单独构成语句,因此极少使用其返回值。
2 . 格式说明项
格式说明项的一般形式为: 
% é标志. é最小宽度. é.精度. é长度. 格式字符
由一般形式可以看出,格式说明项必须以%开头,以格式字符结尾,中间是若干可选项, 
根据需要选择输出宽度、精度等控制信息。
(1)格式字符:用于指定输出数据的类型,常用格式字符及其功能描述如表3-1所示。
同一个功能可能会有多个格式字符,如输出十进制整数,d、i均可,输出一个字符,c、C均可。
但为了提高程序可读性和减少记忆负担,建议初学者统一使用第一个小写字符。
(2)标志:标志字符有-、+、#、空格、0共5种,其功能描述如表3-2所示。
(3)最小宽度:用十进制整数表示所输出数据占据的最少位数。如果实际位数多于定
义宽度,则按实际位数输出,若实际位数少于定义宽度,则补以空格或0。
(4)精度:如果输出数据为实数,则表示小数的位数;如果输出的是字符串,则表示输

67 
出字符的个数。
表3-1 常用格式字符及其功能描述
数据类型格式字符功能描述
int 
d或i 以十进制形式输出带符号整数(默认正数不输出符号) 
o 以八进制形式输出无符号整数(默认不输出前缀0) 
x或X 以十六进制形式输出无符号整数(默认不输出前缀0x或0X) 
u 以十进制形式输出无符号整数
char c或C 输出一个字符
s 输出一个字符串
floatdouble 
f 以小数形式输出单、双精度带符号实数,默认小数位数为6位
e或E 以指数形式输出单、双精度实数
g或G 以%f和%e中较短的输出宽度输出单、双精度实数
其他% 输出符号% 
表3-2 标志字符功能描述
标志字符功能描述
- 表示输出结果左对齐,若结果不够指定宽度,则右边补充空格;默认为右对齐
+ 输出数据前冠以符号(正号或负号);默认只有负数前有符号
# 加在格式字符o(八进制)前,则输出结果加前缀o;加在格式字符x或X(十六进制)前,则
输出结果加前缀0x或0X 
空格输出数据为正时,前面冠以空格
0 若实际位数少于定义宽度,则左边补0,但与'-'一起使用时不起作用,此时右边填充空格 
(5)长度:长度格式符有h和l两种,一般用于整型格式字符前,h表示按短整型输出; 
l表示按长整型输出,例如,ld表示按长整型输出,lld按长长整型输出。l如果用于f前,即
lf则表示按double型输出。
【例3-2】 printf格式输出综合测试程序。 
#include <stdio.h> 
int main() 
{ 
int datai=66; 
char datac='a'; 
float dataf=138.3576278; //精度超出float 的7 位,会有误差 
double datad=138.3576278; //精度在double 的范围内,没有误差 
//以不同进制形式输出datai 的值66 
printf("十进制: %d,八进制: %o,十六进制: %x\n",datai,datai,datai); 
printf("datai=%+d,-datai=%+d\n",datai,-datai); //输出带符号整数 
printf("datai=%-5d,%5d\n",datai,datai); //按指定宽度5,左、右对齐输出 
printf("datac=%c,ASCII=%d\n",datac,datac); //按字符和整数输出字符型数据 
printf("dataf=%f,dataf=%10.4f\n",dataf,dataf); //按指定宽度与精度输出实数 
printf("datad=%lf\n",datad); //输出双精度实数
}

68 
程序运行结果为:(//后面为对结果的解释说明) 
十进制: 66,八进制: 102,十六进制: 42 
datai=+66,-datai=-66 //%+d 是指定数据无论正负都输出符号,%d 只是负数才输出符号
datai=66□□□,□□□66 //%-5d 为左对齐,%5d 为右对齐,不足用空格补齐
datac=a,ASCII=97 //字母a 的ASCII 码为97 
dataf=138.357620,dataf=□□138.3576 //%10.4f 指总体宽度10,精度4,小数点占1 位
datad=138.357628 //%ld 输出double 型数据
学完此小节,可以解决计算瓷砖数案例中的问题“tiles中的值如何输出到屏幕上”,一
种解决方案为: 
printf("tiles=%d",tiles); 
注意:语句中的两个tiles含义完全不同,第一个tiles是格式控制串中的普通字符,输
出时原样输出;第二个tiles是变量名,表示其值代替%d输出。
3.3.2 格式输入函数scanf 
1 . scanf 函数调用方法 
scanf函数是与printf函数相对应的一个标准输入库函数,其功能为按指定格式从键盘
输入数据,并存放到指定地址的内存中。同样,scanf函数为了解决复杂的数据输入,其语法
复杂、琐碎,稍不留神就出错,因此建议初学者使用最简单的输入方式,不需酷炫效果。
scanf函数的函数原型为: 
int scanf(const char *format é,argument…. ) 
scanf和printf的函数原型非常相似,参数format仍为格式控制串,多为常量字符串。
参数argument同样可有可无,但此处为地址列表。
例如: 
(1)格式控制串。用于指定数据的接收类型和格式,同样包括格式说明项和普通字符
两种信息。格式说明项用于指定接收类型,如上例中的%d指定按一个整数接收数据,%c 
指定按一个字符接收数据。普通字符用于指定接收格式,“a=”和“,b=”需原样输入。
(2)地址列表。用于指定存放输入数据的内存地址。具体形式多种多样,如变量地址, 
数组地址或指针变量,甚至是三者组成的表达式,只要结果是一个地址就可以。如上例中, 
要把输入数据存放在变量a 中,则需指定变量a 的地址:&a。
上例中“scanf("a=%d,b=%c",&a,&b);”,格式控制串为字符串常量“a=%d, 
b=%c”,说明输入时“a=”“,b=”要原样输入,而%d和%c的位置分别用一个整数和一个
扫一扫

69 
字符代替。假设输入信息如下(CR表示回车键Enter): 
a=36,b=r<CR> 
则运行结果为:36,被存放在变量a 中,字符r' '被存放在变量b 中。
特别提醒:使用scanf输入数据时,一定要和指定的格式保持一致,否则变量无法接收
到正确数据,但又不会提示任何错误信息,因此会造成程序逻辑错误。例如,上例中如果输
入“36r<CR>”“36□r<CR>”或“a=36b=r<CR>”等,均不能正确接收。提醒初学者用
最简单的输入语句即可。
函数返回值。scanf函数的返回值为成功接收数据的项数,出错时则返回EOF,即-1。
【例3-3】 根据函数返回值判断正确接收数据的个数。 
#include <stdio.h> 
int main() 
{ 
int a,b,number; 
number=scanf("a=%d,b=%c",&a,&b); //将scanf 返回值赋给number 
printf("正确接收数据数: %d\n",number); 
}
程序运行结果为: 
输入: a=36,b=r<CR> 
输出: 正确接收数据数: 2 
输入格式与指定格式完全一致,a、b均正确接收。 
输入: a=36b=r<CR> 
输出: 正确接收数据数: 1 
因为a=36与指定格式一致,a 能正确接收,但“b=r<CR>”因为b 前少了一个“,”, 
与指定格式不一致,b 不能正确接收。 
输入: 36r<CR> 
输出: 正确接收数据数: 0 
因为a、b 均不能正确接收。
2 . 格式说明
格式说明项的一般形式为: 
% é*. é宽度. é长度. 格式字符
scanf函数和printf函数的格式说明项极其相似,仍然必须以%开头,以格式字符结尾, 
但中间控制信息比较简单,尤其注意没有精度信息,说明输入时不能指定输入数据的精度, 
例如,假设变量c 为float类型: 
scanf("3.1%f",&c);

70 
这样的语句无论输入什么数据,变量c 都无法正确接收到,而程序又不会提示任何错误
信息,因此一定避免使用。
(1)格式字符:用于指定输入数据的数据类型,常用的部分格式字符和功能描述如
表3-3所示。建议初学者统一使用第一个小写字符。
表3-3 常用格式字符功能描述表
数据类型格式字符功能描述
int 
d或D或i或I 输入十进制整数
o 输入八进制整数
x或X 输入十六进制整数
u或U 输入无符号十进制整数
char c或C 输入一个字符
s 输入一个字符串
float f或e或E 输入实型数(用小数形式或指数形式) 
【例3-4】 输入一个字符,输出其ASCII码值。 
#include <stdio.h> 
int main() 
{ 
char c; 
printf("请输入一个字符: "); 
scanf("%c",&c); //输入一个字符,存放到变量c 中 
//输出列表中有两个c ,第一个按字符输出,第二个按整型输出 
printf("%c 的ASCII 为: %d\n",c,c); 
}
举一反三: 
① 输入一个整数,输出对应的字符。
② 输入一个十或八进制数,输出其对应的十六进制数。
(2)“*”符:表示跳过对应的输入数据。例如: 
scanf("%d%*d%d",&a,&b); 
当输入如下信息时: 
30□15□6<CR> 
系统会将30赋给a,6赋给b,而15被跳过,不赋给任何变量。
【例3-5】 用*忽略整数和字符之间的间隔符。 
#include <stdio.h> 
int main() 
{

71 
int age; 
char sex; 
printf("请输入年龄和性别(M 或F),空格分开: "); 
scanf("%d%*c%c",&age,&sex); //忽略中间间隔符 
printf("年龄是: %d,性别是: %c\n",age,sex); 
}
程序运行结果为: 
输入: 18□F<CR> 
输出: 年龄是: 18,性别是: F 
输入数据时,我们习惯性地将两个数据用间隔符(空格、Tab键和回车键)分开,但用%c 
接收数据时,间隔符也会当正常字符一样被接收。例3-5中的输入语句如果写成: 
scanf("%d%c",&age,&sex); 
同样输入: 18□F<CR> 
则sex会接收空格(□),而不是F。因此常常使用类似“%*c”的形式忽略间隔符。
(3)宽度:用十进制正整数指定输入数据的宽度。例如: 
scanf("%3d%2d",&a,&b); 
输入: 123456<CR> 
则123赋给变量a,45赋给变量b,最后的6会留在缓冲区中,用于下一个输入。 
输入: 1234<CR> 
则123赋给a,4赋给b。即使输入数据不够所需位数,遇到回车也终止接收。 
输入: 1<CR>2345<CR> 
则1赋给a,23赋给b,45留在缓冲区。
【例3-6】 日期格式转换:输入日期20220520,转换为2022年5月20日。 
#include <stdio.h> 
int main() 
{ 
int year,month,day; 
printf("请按样例格式输入一个日期: 20220520\n"); 
scanf("%4d%2d%2d",&year,&month,&day); //按位数截取年月日 
printf("%d 年%d 月%d 日\n",year,month,day); 
}
程序运行结果为: 
输入: 20220520 
输出: 2022 年5 月20 日

72 
(4)长度:长度格式符有l和h两种,l表示输入长整型数据(如%ld,%lo,%lx)或双精
度浮点数(如%lf,%le);h表示输入短整型数据(如%hd,%ho,%hx)。
3 . 使用scanf 函数时需注意的问题
(1)特别注意scanf函数中的第二个参数必须是地址列表。
【例3-7】 地址列表错误示例。 
#include <stdio.h> 
int main() 
{ 
int a=0; 
scanf("%d",a); //a 前缺少& 
}
则程序会出现运行错误,程序终止运行。Dev-C++中的错误提示如图3-2所示。
图3-2 缺少& 时的运行错误提示信息
单击“联机检查解决方案并关闭该程序”选项,则会发现程序的返回值为3221225477, 
如图3-3所示。
图3-3 返回错误编码图
此处的错误编码3221225477(0xC0000005)表示访问越界。访问越界是指访问了不属
于自己的内存空间。内存就像国土一样,绝不允许非法占用。应用程序运行时,操作系统要
为其变量分配内存空间,只有分配给该应用程序的内存可以随意存取数据,但绝对不可以向
其他内存中存放数据。而此例中,数据本来应该存放到首地址为&a,即为a分配的内存中
去,结果却存放到了首地址为a,即地址为0的内存中去,而首地址为a的空间并不属于该应
用程序,造成了越界访问。
(2)当使用scanf函数从键盘输入数据完毕后,一定要按下回车键,函数才会接收到数
据。其实scanf函数,以及后面要介绍的getchar函数,都是缓冲输入函数,即并不是直接接

73 
收键盘数据,而是将用户输入的数据暂时存放在缓冲区中,当用户键入回车或缓冲区满了之
后,scanf函数才会从缓冲区中依次读取数据。如果缓冲区中的数据已经读取完,但仍需数
据,则函数会等待输入,直到满足函数要求或遇到非法数据为止;如果输入的数据多于函数
要求输入的数据,则多余的数据将留在缓冲区中,供下一个输入使用。
(3)当scanf中的两个相邻格式说明项紧密相连时,一般遇到以下三种情况之一,则认
为当前数据结束: 
① 遇到间隔符(空格、Tab键和回车键)。
② 遇到宽度限制,如%3d,只取3列。
③ 遇到非法数据。
例如: 
scanf("%d%c",&a,&b); 
输入: 12c34<CR> 
则会把12赋给变量a,把字符c赋给变量b,而34留在缓冲区中。 
输入: 12□c34<CR> 
则会把12赋给变量a,把字符空格□赋给变量b,而c34留在缓冲区中。因为间隔符也
会被看作一个字符,正常接收。因此在C语言中不建议%c与其他格式符连用,否则特别容
易出现莫名其妙的错误。
例如: 
scanf("%d%d",&a,&b); 
输入:12□34<CR>,12<Tab>34<CR>,12<CR>34<CR>,三种方式均能正确
接收,即a接收12,b接收34。 
输入: 12c34<CR> 
则a接收12,因为字母c对于整数是非法数据,因此认为数据结束,这样b就无法接收
到值,即保持原值不变。
(4)如果格式控制串中有普通字符串,即有多个紧密相连的普通字符,则输入数据时严
格按照“普通字符串前不加间隔符”(即如果格式控制串为:普通字符串+格式说明项,则普
通字符串和数据之间可以加入若干间隔符;如果格式控制串为:格式说明项+普通字符串, 
则数据和普通字符串之间不能加入任何间隔符)的原则输入即可,除非格式说明项中有宽度
限制。例
如: 
scanf("a=%d,b=%d",&a,&b); 
使用该语句把46接收到变量a 中,把18接收到变量b 中,则下面几种输入方式均合法
(其中□代表空格字符): 
①a=46,b=18<CR>:不加任何间隔符,严格遵循输入格式。

74 
②a=□46,b=18<CR>:普通字符串a=和数据46间加入若干个空格间隔符。
③a=<Tab>46,b=□18<CR>:普通字符串a=和数据46间加入若干个Tab间
隔符。④
a=46,b=18□<CR>:数据18后面加入若干个空格、Tab等间隔符。
而下面两种情况则不能正确接收: 
① <Tab>a=46,b=18<CR>:普通字符串a=前加入Tab间隔符,违背输入原则。
②a=46,□b=18<CR>:在普通字符串“,b=”中插入一个空格,导致与普通字符串
不匹配。
(5)用%c输入字符时,间隔符都会作为有效字符输入,而不再起间隔数据的作用。如: 
scanf("%c%c",&a,&b); 
如果输入:c□d<CR>,则变量a 接收字符c,变量b 接收字符空格,字符d留在缓冲
区中。如
果输入:c<CR>,则变量a 接收字符c,变量b 接收字符回车。
学完此小节,可以解决计算瓷砖数案例中的问题“area等变量的值如何从键盘输入”。
一种解决方案为: 
scanf("%f%f%f",&area,&length,&width); 
第二种解决方案为: 
scanf("area=%f,length=%f,width=%f",&area,&length,&width); 
注意:这两种解决方案在具体输入时一定严格按照规定格式输入,否则无法接收到正
确数据。一定不要忘记各个变量前的&,否则程序会出现运行错误。
3.3.3 单字符输出函数putchar 
putchar是一个功能单一、使用简单的字符输出函数,其函数原型为: 
int putchar(int ch) 
功能:向标准输出设备(显示器)输出一个字符。
(1)参数ch表示要输出的字符,字符应该为char类型,为什么此处为int呢? 在计算
机中,字符其实是以其对应ASCII码值形式存在的,char类型数据的存储、运算都会隐式转
换为int类型。因此,此处的实际参数具体形式可能千变万化,但本质是一个整数,只要表
达式最终能转换为一个整数即可。
【例3-8】 putchar函数参数测试程序。 
#include <stdio.h> //把头文件stdio.h 包括到文件中
int main() 
{ 
char a='A'; //定义字符型变量 
int b=65; //定义整型变量,65 是字母A 的ASCII 码值
扫一扫

75 
putchar(a+1); //参数为字符型表达式,可自动隐式转换为整型。输出字母B 
putchar(b+1); //参数为整型表达式,输出字母B 
putchar('B'); //参数为字符型常量,可自动隐式转换为整型。输出字母B 
putchar('\102'); //参数为转义字符,注意转义符中的102 是八进制数 
putchar(7); //有ASCII 码的不可见字符也可输出,7 为一次响铃。
}
程序输出结果为: 
BBBB<响铃> 
本程序除响铃语句外,每条输出语句的输出结果相同,均为字母B,但具体表达形式不
同,以期为读者提供正确的使用参考。
特别提醒:虽然参数ch为int类型,但ASCII码只有256个(除附录A 中的128个外
还有128个扩展字符)。因此,当ch的值超过255时,会自动进行取余运算:ch%256,使ch 
始终在正确范围内。因此putchar(263)和putchar(7)的功能是一样的。
(2)函数返回值类型为整型,如果输出成功,则返回输出字符的ASCII码值,即参数
ch;若输出失败,则返回EOF(-1)。
putchar(ch)与printf("%c",ch)的功能等效,都是输出一个字符,经常替换使用,但返
回值含义不同:putchar返回的是输出字符的ASCII码值,而printf返回的是输出字符的个
数,此处永远为1。
3.3.4 单字符输入函数getchar 
getchar函数与putchar函数是一对,同样功能单一,使用简单,其函数原型为: 
int getchar() 
功能:从标准输入设备(键盘)上输入一个字符。
getchar函数是一个无参函数,如果正确接收,则函数的返回值为输入字符的ASCII码
值,否则返回EOF(-1)。
【例3-9】 输入一个字符,输出它的后继字符。
程序分析:一个字符的后继字符是指其在ASCII码表中的后一个字符,如字母A 的后
继字符是B,d的后继字符是e。一个字符和其后继字符之间的ASCII码值差1。 
#include <stdio.h> 
int main() 
{ 
char ch; 
ch=getchar(); //接收一个字符 
printf("%c 的后继字符是: %c\n",ch,ch+1); //输出其后继字符
}
使用getchar函数时需要注意以下问题: 
(1)getchar函数是一个缓冲输入函数,输入字符后必须按回车键才能接收数据。
扫一扫

76 
(2)如果接收的是一个数字,也会按照字符处理。例如程序输入:1<CR>,则程序运
行结果为“1的后继字符是:2”。即接收的是1这个数字的ASCII码值,即49。
(3)空格、Tab键以及回车键等都会作为一个getchar函数能接收的有效字符。例如程
序输入:<CR>,则程序运行结果如图3-4所示。
图3-4 接收回车键时的程序结果
(4)如果输入多于一个字符,则只接收第一个字符。例如程序输入:32<CR>,则程序
运行结果为“3的后继字符是:4”,另一个字符2将留在缓冲区中。
例3-9中的ch=getchar()与scanf("%c",&ch)的功能基本等效,都是输入一个字符放
在变量ch中,区别在于:scanf的返回值为成功接收数据的项数,此处为1或0,而getchar 
的返回值为接收字符的ASCII码值。在某些应用中,用getchar实现的程序代码更简洁,可
读性更强,如5.1节中的例5-2,6.4.2节中的例6-15等,读者学习到相关内容时再深入体会
即可。
3.3.5 不回显输入函数getch 
getch不是一个标准库函数,它位于conio.h中。conio是ConsoleInput/Output(控制
台输入输出)的简写,其中定义了通过控制台进行数据输入和数据输出的一些函数。conio.h 
是Windows系统特有的,其他操作系统中不一定提供,因此可移植性较差。
函数原型为: 
int getch() 
从函数原型看,getch与getchar的调用形式完全相同,功能也类似,都是从标准输入设
备(键盘)上输入一个字符,但主要有以下两点区别。
(1)getch为不回显函数,即用户输入的字符只默默接收,但不显示在屏幕上。
(2)getch为非缓冲输入函数,即有输入时立即接收,不需要按回车键。
getch经常应用于以下两种场景中。
(1)实现暂停功能,解决闪退或闪过问题。在有些开发环境中运行C语言程序或者在
操作系统中直接执行C语言生成的.exe文件时,会出现一闪就退出的现象,无法看清执行
结果;或者程序输出数据过多,一些有用信息一闪而过。这时都可以加一个getch语句,留
住当前界面,实现“按任意键继续”功能。
(2)实现密码输入功能。用户输入密码时一般不显示真正的输入字符,而用特殊符号
代替。
【例3-10】 编程实现,利用getch实现一位密码输入功能。
扫一扫

77 
#include <stdio.h> 
#include <conio.h> //包含相应头文件
int main() 
{ 
int ch; 
printf("请输入一位密码: "); 
ch=getch(); //接收字符,但屏幕不显示 
putchar('*'); //屏幕输出替代字符 
printf("\n 您输入的密码是: %c",ch); 
}
程序运行结果如图3-5所示。
图3-5 程序运行结果图
3.4 其他常用函数 
为更便捷、更高效地使用C语言,标准函数库中提供了众多函数,读者只需使用#include 
命令将其对应的头文件包含到程序中,然后按照函数原型说明使用即可。函数原型就像一
份函数使用说明书,让读者清楚函数需要几个参数,参数的数据类型是什么,函数执行完毕
后会返回一个什么数据类型的值。
本节内容初学时只需浏览框架,后期用到时再返回本节仔细阅读即可。
3.4.1 常用数学函数
数学函数对应的头文件是math.h。
(1)求双精度浮点数绝对值函数fabs(求整数绝对值函数为abs)。
函数原型:doublefabs(doublex)。
函数功能:求x 的绝对值。
通过函数原型可以看出,fabs函数需要一个double类型的参数,实际应用时,参数可以
是一个常量、变量或者表达式,只要最终结果值的数据类型为或者能隐式转换为double即
可。函数返回值为double类型,说明其结果可以参与double类型数据能参与的一切运算。
【例3-11】 编程实现:求数轴上两点之间的距离。 
#include <stdio.h> 
#include <math.h> 
扫一扫

78 
int main() 
{ 
double x=2.3,y=8.9,distance; //x、y 为数轴上的两个点 
/*将x-y 的值,即-6.6 传递给函数fabs;因为fabs 函数的返回值类型为double,因此
需要定义一个double 类型的变量distance 存放其返回值*/ 
printf("distance=%lf\n",fabs(x-y)); //注意输出时一定用%lf 
(2)求平方根函数sqrt。
函数原型:doublesqrt(doublex)。
函数功能:计算x 的平方根。
【例3-12】 编程实现:求平面中两点之间的距离。 
#include <stdio.h> 
#include <math.h> 
int main() 
{ 
float x1,y1,x2,y2,distance; //点1(x 1,y 1) 点2(x 2,y 2) 
printf("请输入第一个点: 空格分开"); 
scanf("%f%f",&x1,&y1); 
printf("请输入第二个点: 空格分开"); 
scanf("%f%f",&x2,&y2); 
distance=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); //距离公式 
printf("distance=%f\n",distance); 
}
(3)上取整函数ceil。
函数原型:doubleceil(doublex)。
函数功能:求不小于x 的最小整数,即上取整。
【例3-13】 编程实现:根据铺设瓷砖的面积(平方米),所选择瓷砖的尺寸(厘米×厘
米),计算需要瓷砖的块数。 
#include <stdio.h> 
#include <math.h> 
int main() 
{ 
float area,length,width; 
int tiles; //瓷砖块数,整型变量 
printf("请输入铺设面积(平方米):") ; 
scanf("%f",&area); 
printf("请输入瓷砖的长度和宽度(厘米):"); 
scanf("%f%f",&length,&width); 
//ceil 返回值为double 型,赋给整型变量tiles 时,最好加一个强制类型转换 
tiles=(int)ceil(area*100/(length*width)); 
printf("大约需要瓷砖块数: %d",tiles); 
}

79 
(4)求方函数pow。
函数原型:doublepow(doublex,doubley)。
函数功能:计算x 的y 次方的值。
【例3-14】 编程实现:计算n 位二进制位能表示的最大整数。 
#include <stdio.h> 
#include <math.h> 
int main() 
{ 
int n,maxint; 
printf("请输入二进制位的位数: "); 
scanf("%d",&n); 
maxint=(int)pow(2,n-1)-1; //n 位二进制位能表示的最大整数2n -1-1 
printf("%d 位能表示的最大整数为: %d",n,maxint); 
}
例如,整型变量占4B,32位,程序执行结果为: 
输入: 32 
输出: 32 位能表示的最大整数为: 2147483647 
3.4.2 常用字符函数
字符函数对应的头文件为ctype.h。
(1)小写字母转大写函数toupper。
函数原型:inttoupper(intc)。
函数功能:如果c有对应的大写字母,则返回大写字母,否则返回原值。
(2)大写字母转小写函数tolower。
函数原型:inttolower(intc)。
函数功能:如果c有对应的小写字母,则返回小写字母,否则返回原值。
【例3-15】 编程实现:如果一个字符是小写字母,则转换为大写字母,否则保持不变。 
#include <stdio.h> 
#include <ctype.h> 
int main() 
{ 
char ch; 
printf("请输入一个小写字母: "); 
ch=getchar(); 
putchar(toupper(ch)); 
}
程序执行结果为: 
输入: a //输入是小写字母
输出: A //则转换为大写字母
输入: 3 //输入不是小写字母
输出: 3 //则保持不变
扫一扫

80 
(3)检查字母函数isalpha。
函数原型:intisalpha(intc)。
函数功能:判断字符c是否为英文字母,若是,则返回非0(小写字母为2,大写字母为
1),否则返回0。
例如:isalpha('*')的值为0,isalpha('E')的值为1,而isalpha(e' ')的值为0。
3.4.3 其他常用工具函数
(1)获取系统时间函数time,包含在头文件time.h中。
函数原型:time_ttime(time_t*t)。
函数功能:获取计算机的当前日历时间。日历时间是指从一个时间点(一般是1970年
1月1日0时0分0秒)到现在的秒数。
函数中用到的数据类型time_t,实质是整型,为了提高程序的通用性,一般将32位编译
器中的long(长整型),将64位编译器中的__int64(即长长整型)取别名为time_t。
函数获取时间的方式既可以通过参数t,也可以通过返回值,甚至参数和返回值同时获
取也可以,但重复数据也没有实际意义。建议读者采用返回值形式,此时参数设置为空
(NULL)即可。
【例3-16】 编程实现:获取自时间纪元至今的秒数和年数。 
#include <stdio.h> 
#include <time.h> 
int main() 
{ 
time_t curtime; //定义接收当前时间的变量,也可以定义为long 型 
curtime=time(NULL); //用返回值形式返回日历时间,参数为NULL 
printf( "时间纪元已经飞逝了%ld 秒, 约%ld 年", curtime, curtime/(24* 60* 60* 
365)); 
}
因为当前时间在不断变化,所以程序的运行结果中的秒数每次均不同。
(2)产生一个伪随机数函数rand,包含在头文件stdlib.h中。
函数原型:intrand()。
函数功能:产生一个范围在0~RAND_MAX的伪随机整数,其中RAND_MAX是一
个符号常量,在stdlib.h中可以找到其定义“#defineRAND_MAX0x7fff”。0x7fff就是
32767,即短整型的最大值,因此产生的就是一个短整型非负数。
(3)随机数生成器初始化函数srand,包含在头文件stdlib.h中。
函数原型:voidsrand(unsignedseed)。
函数功能:为随机数生成器提供一粒新的随机种子。
如果随机种子相同,rand产生的随机数也是相同的。为真正达到每次产生的随机数不
同,需要在调用rand之前用srand设置不同的随机种子。一般采用time函数产生的系统时
间作为随机种子,因为系统时间精确到秒,变化很快,基本能满足程序多次执行时种子不同
的需求。
【例3-17】 编程实现:随机产生一个三位整数。
扫一扫