【C语法学习】12 - scanf()函数

文章目录

  • [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()函数的参数分为两类:

  1. format :格式字符串;
  2. ... :参数列表。

2.1 格式字符串

在scanf()函数中:

  1. 格式字符串是必不可少的;
  2. 格式字符串由字面文本量、转义序列和转换说明三部分组成。

2.1.1 转换说明

  1. 格式字符串中的转换说明和参数列表在数量、顺序和类型上要完全匹配;
  2. 格式字符串中只包含转换说明即可,强烈建议不要包括字符串字面量和转义序列,但多个转换说明之间可以用空格隔开,便于阅读;
  3. 如果想从键盘获取字符,建议用getchar()函数替代%c;
  4. 如果想从键盘获取字符串,建议用gets()函数替代%s;%s会读取的字符串末尾加上空字符'\0',作为字符串结束符;
  5. 与printf()函数不同,在scanf()函数中,%f对应float类型变量,%lf对应double类型变量。

2.2 参数列表

在scanf()函数中:

  1. 参数列表是必不可少的;
  2. 参数列表中参数的数量是可变的,但至少应包含一个参数;
  3. 参数列表中参数的类型是指针类型(&变量名)。

3 返回值

scanf()函数的返回值类型为int型:

  1. 读取成功,返回成功读取的项数;
  2. 读取失败,返回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 基本概念

  1. 键盘输入:从键盘输入的都是文本,因为键盘只能生成文本字符:字母、数字和标点符号;
  2. 空白字符:空格、制表符和换行符。

4.2 转换说明

转换说明不同,有效字符也不同:

  1. %d转换说明要求scanf()函数能识别十进制数0-9和正负号±;
  2. %x转换说明要求scanf()函数能识别十六进制数a-f和A-F;
  3. %f转换说明要求scanf()函数能识别小数点、e计数法和p计数法等;
  4. %s转换说明要求scanf()函数能识别除空白字符外的所有字符。

4.3 读取过程

scanf()函数读取标准输入流stdin过程如下:

  1. 在遇到第一个非空白字符前,所有的空白字符被读出且被丢弃;
  2. 从遇到第一个非空白字符始,直至
    (1)遇到无效字符:在遇到的第一个无效字符处停止读取,并将无效字符退回至标准输入流stdin中;
    (2)遇到空白字符:在遇到的第一个空白字符处停止读取,并将空白字符退回至标准输入流stdin中;
    (3)达到指定字段宽度:如果使用字段宽度修饰符,在有效字符数达到指定字段宽度处停止读取;
  3. 读取结束后对已读取的有效字符按照转换说明进行转换,然后储存在指定变量中。

特别说明:使用scanf()函数之后一定要清空标准输入流stdin。

4.4 读取示例

以scanf("%d", &num)为例:

  1. scanf()函数从标准输入流stdin中读取字符,跳过前面所有的空白字符,从第一个非空白字符开始,直至
    (1)遇到无效字符,将无效字符退回至标准输入流stdin中,读取结束;
    (2)遇到空白字符,将空白字符退回至标准输入流stdin中,读取结束;
    (3)达到指定字段宽度,读取结束;
  2. 将已读取的有效字符按照转换说明要求进行转换,并储存在指定的变量num中;

问题:如果第一次读到的非空白字符是无效字符(如字母a),会发生什么情况呢?

解答:如上所述,如果读到字母a,则立即停止读取,并将字母a退回stdin中,那么本次读取就未读到任何有效的字符,也就是说本次读取是失败的,不会有任何值储存在变量num中,scanf()函数的返回值为0。

4.5 多参数

参数列表中的多个参数对应格式字符串中的多个转换说明:

  1. 用空白字符将键盘输入分成多个字段;
  2. 字段间可以有多个空白字符,多个空白字符集中在一行或分散在多行;
  3. 多个字段与多个转换说明;
  4. 唯一例外的是%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;
}

代码执行结果如下图所示:

代码及运行结果分析:

第一阶段:

  1. 标准输入流中有字符:2个空格+字符12a+2个空格+回车符共8个字符;
  2. scanf()函数读取前两个空格并丢弃;
  3. scanf()函数读取字符1和2,转换为数值12后储存在变量a中;
  4. scanf()函数读取字符a,发现a是无效字符,将a回退至标准输入流stdin中;
  5. 至此scanf()函数从标准输入流stdin中读取完毕;

第二阶段:

  1. gets()函数继续从标准输入流stdin中读取字符串;

  2. 因标准输入流stdin中仍残留有scanf()函数未读完的字符,故无需用户再次从键盘键入字符;

  3. gets()函数会读空标准输入流stdin中的所有字符,直至遇到回车符'\n';

第三阶段:

  1. 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;
}

代码执行结果如下图所示:

代码及运行结果分析:

  1. 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;
}

代码执行结果如下图所示:

代码及运行结果分析:

  1. 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;
}

代码执行结果如下图所示:

代码及运行结果分析:

  1. scanf()函数在读取字符的时候,如果遇到空白字符,则停止读取,所以%s只能读取单词,不能读取句子。
相关推荐
就爱学编程1 小时前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
北国无红豆2 小时前
【CAN总线】STM32的CAN外设
c语言·stm32·嵌入式硬件
单片机学习之路2 小时前
【C语言】结构
c语言·开发语言·stm32·单片机·51单片机
graceyun3 小时前
C语言初阶习题【9】数9的个数
c语言·开发语言
Schwertlilien5 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习
程序员buddha6 小时前
C语言从入门到放弃教程
c语言·开发语言
AAA.建材批发刘哥10 小时前
Linux快速入门-Linux文件系统管理
linux·运维·服务器·c语言·学习方法
Kisorge12 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
爱吃西瓜的小菜鸡15 小时前
【C语言】判断回文
c语言·学习·算法
FeboReigns18 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++