文章目录
- [0 前言](#0 前言)
- [1 函数原型](#1 函数原型)
- [2 参数](#2 参数)
-
- [2.1 格式字符串](#2.1 格式字符串)
-
- [2.1.1 转换说明](#2.1.1 转换说明)
- [2.2 参数列表](#2.2 参数列表)
- [3 返回值](#3 返回值)
- [4 读取机制](#4 读取机制)
-
- [4.1 基本概念](#4.1 基本概念)
- [4.2 转换说明](#4.2 转换说明)
- [4.3 读取过程](#4.3 读取过程)
- [4.4 读取示例](#4.4 读取示例)
- [4.5 多参数](#4.5 多参数)
- [6 示例](#6 示例)
-
- [6.1 示例1](#6.1 示例1)
- [6.2 示例2](#6.2 示例2)
- [6.3 示例3](#6.3 示例3)
- [6.4 示例4](#6.4 示例4)
0 前言
scanf()函数虽然使用起来较为灵活,但是其读取机制还是有点复杂。
1 函数原型
scanf():从标准输入流stdin读取格式化输入,函数原型如下:
c
int scanf(const char *format, ...);
2 参数
scanf()函数的参数分为两类:
- format :格式字符串;
- ... :参数列表。
2.1 格式字符串
在scanf()函数中:
- 格式字符串是必不可少的;
- 格式字符串由字面文本量、转义序列和转换说明三部分组成。
2.1.1 转换说明
- 格式字符串中的转换说明和参数列表在数量、顺序和类型上要完全匹配;
- 格式字符串中只包含转换说明即可,强烈建议不要包括字符串字面量和转义序列,但多个转换说明之间可以用空格隔开,便于阅读;
- 如果想从键盘获取字符,建议用getchar()函数替代%c;
- 如果想从键盘获取字符串,建议用gets()函数替代%s;%s会读取的字符串末尾加上空字符'\0',作为字符串结束符;
- 与printf()函数不同,在scanf()函数中,%f对应float类型变量,%lf对应double类型变量。
2.2 参数列表
在scanf()函数中:
- 参数列表是必不可少的;
- 参数列表中参数的数量是可变的,但至少应包含一个参数;
- 参数列表中参数的类型是指针类型(&变量名)。
3 返回值
scanf()函数的返回值类型为int型:
- 读取成功,返回成功读取的项数;
- 读取失败,返回EOF。
C语言标准描述如下:
1. Both scanf and wscanf return the number of fields successfully converted and assigned; the return value does not include fields that were read but not assigned. A return value of 0 indicates that no fields were assigned.
2. The return value is EOF for an error or if the end-of-file character or the end-of-string character is encountered in the first attempt to read a character.
4 读取机制
4.1 基本概念
- 键盘输入:从键盘输入的都是文本,因为键盘只能生成文本字符:字母、数字和标点符号;
- 空白字符:空格、制表符和换行符。
4.2 转换说明
转换说明不同,有效字符也不同:
- %d转换说明要求scanf()函数能识别十进制数0-9和正负号±;
- %x转换说明要求scanf()函数能识别十六进制数a-f和A-F;
- %f转换说明要求scanf()函数能识别小数点、e计数法和p计数法等;
- %s转换说明要求scanf()函数能识别除空白字符外的所有字符。
4.3 读取过程
scanf()函数读取标准输入流stdin过程如下:
- 在遇到第一个非空白字符前,所有的空白字符被读出且被丢弃;
- 从遇到第一个非空白字符始,直至
(1)遇到无效字符:在遇到的第一个无效字符处停止读取,并将无效字符退回至标准输入流stdin中;
(2)遇到空白字符:在遇到的第一个空白字符处停止读取,并将空白字符退回至标准输入流stdin中;
(3)达到指定字段宽度:如果使用字段宽度修饰符,在有效字符数达到指定字段宽度处停止读取; - 读取结束后对已读取的有效字符按照转换说明进行转换,然后储存在指定变量中。
特别说明:使用scanf()函数之后一定要清空标准输入流stdin。
4.4 读取示例
以scanf("%d", &num)为例:
- scanf()函数从标准输入流stdin中读取字符,跳过前面所有的空白字符,从第一个非空白字符开始,直至
(1)遇到无效字符,将无效字符退回至标准输入流stdin中,读取结束;
(2)遇到空白字符,将空白字符退回至标准输入流stdin中,读取结束;
(3)达到指定字段宽度,读取结束; - 将已读取的有效字符按照转换说明要求进行转换,并储存在指定的变量num中;
问题:如果第一次读到的非空白字符是无效字符(如字母a),会发生什么情况呢?
解答:如上所述,如果读到字母a,则立即停止读取,并将字母a退回stdin中,那么本次读取就未读到任何有效的字符,也就是说本次读取是失败的,不会有任何值储存在变量num中,scanf()函数的返回值为0。
4.5 多参数
参数列表中的多个参数对应格式字符串中的多个转换说明:
- 用空白字符将键盘输入分成多个字段;
- 字段间可以有多个空白字符,多个空白字符集中在一行或分散在多行;
- 多个字段与多个转换说明;
- 唯一例外的是%c转换说明:根据%c,scanf()函数会读取每个字符,包括空白字符。
6 示例
6.1 示例1
scanf()函数读取过程中的丢弃和回退,代码如下所示:
c
int main ()
{
//定义变量
int a;
char str[80] = {0};
//给变量a赋值,输入2个空格+12a+2个空格+Enter
scanf("%d", &a);
//打印变量a的值
printf("a=%d\n", a);
//用gets清空stdin
gets(str);
//打印str的内容和长度
printf("str = %s, len = %d\n", str, strlen(str));
return 0;
}
代码执行结果如下图所示:
代码及运行结果分析:
第一阶段:
- 标准输入流中有字符:2个空格+字符12a+2个空格+回车符共8个字符;
- scanf()函数读取前两个空格并丢弃;
- scanf()函数读取字符1和2,转换为数值12后储存在变量a中;
- scanf()函数读取字符a,发现a是无效字符,将a回退至标准输入流stdin中;
- 至此scanf()函数从标准输入流stdin中读取完毕;
第二阶段:
-
gets()函数继续从标准输入流stdin中读取字符串;
-
因标准输入流stdin中仍残留有scanf()函数未读完的字符,故无需用户再次从键盘键入字符;
-
gets()函数会读空标准输入流stdin中的所有字符,直至遇到回车符'\n';
第三阶段:
- printf()函数打印a和str的内容,并统计str的长度;a=12符合预期;str长度为3符合预期,即字符'a'+2个空格。
6.2 示例2
scanf()函数返回值,代码如下所示:
c
void clear_stdin(void)
{
while (getchar() != '\n');
}
int main()
{
//变量定义
int a = 0, b = 0, c = 0;
int num = 0;
//第1次输入
puts("输入11 22 33 : ");
num = scanf("%d%d%d", &a, &b, &c);
clear_stdin();
printf("a=%d, b=%d, c=%d, num=%d\n\n", a, b, c, num);
//第2次输入
puts("输入44 55 a6 : ");
num = scanf("%d%d%d", &a, &b, &c);
clear_stdin();
printf("a=%d, b=%d, c=%d, num=%d\n\n", a, b, c, num);
//第3次输入
puts("输入77 a8 99 : ");
num = scanf("%d%d%d", &a, &b, &c);
clear_stdin();
printf("a=%d, b=%d, c=%d, num=%d\n\n", a, b, c, num);
//第4次输入
puts("输入a0 11 22 : ");
num = scanf("%d%d%d", &a, &b, &c);
clear_stdin();
printf("a=%d, b=%d, c=%d, num=%d\n\n", a, b, c, num);
return 0;
}
代码执行结果如下图所示:
代码及运行结果分析:
- scanf()函数在%d转换说明下,希望从标准输入流stdin中读取数字字符0-9和正负符号(±),当读取到非数字字符'a'时,将'a'退回至标准输入流stdin中,并停止读取;理论分析和num的实际打印结果相符合。
6.3 示例3
scanf()函数读取字符%c,代码如下所示:
c
void clear_stdin(void)
{
while (getchar() != '\n');
}
int main()
{
//变量定义
char c1 = 0, c2 = 0, c3 = 0;
//第1次输入
puts("输入字符abc,中间不加空格 : ");
scanf("%c%c%c", &c1, &c2, &c3);
clear_stdin();
printf("c1=%c, c2=%c, c3=%c\n\n", c1, c2, c3);
//第2次输入
puts("输入字符a b c,中间加空格 : ");
scanf("%c%c%c", &c1, &c2, &c3);
clear_stdin();
printf("c1=%c, c2=%c, c3=%c\n", c1, c2, c3);
return 0;
}
代码执行结果如下图所示:
代码及运行结果分析:
- scanf()函数在%c转换说明下会读取键盘键入的每一个字符,包括空白字符。
6.4 示例4
scanf()函数读取字符串%s,代码如下所示:
c
void clear_stdin(void)
{
while (getchar() != '\n');
}
int main()
{
//变量定义
char str[80] = { 0 };
//
puts("输入hello world");
scanf("%s", str);
clear_stdin();
puts(str);
return 0;
}
代码执行结果如下图所示:
代码及运行结果分析:
- scanf()函数在读取字符的时候,如果遇到空白字符,则停止读取,所以%s只能读取单词,不能读取句子。