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

0 前言

scanf()函数虽然使用起来较为灵活,但是其读取机制还是有点复杂。

1 函数原型

scanf():从标准输入流stdin读取格式化输入,即从键盘输入读取格式字符串,原型如下:

int scanf(const char *format, ...);

2 参数

scanf()函数的参数分为两类:
(1)format:格式字符串;
(2)… :参数列表,类型为指向变量的指针,数量可变。

3 返回值

返回值类型为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 读取机制

基本概念
(1)键盘输入:从键盘输入的都是文本,因为键盘只能生成文本字符:字母、数字和标点符号。
(2)空白字符:空格、制表符和换行符。
读取过程
scanf()函数每次从stdin中读取一个字符:

  1. 在遇到第一个非空白字符前,所有的空白字符被读出且被丢弃;
  2. 从遇到第一个非空白字符始,
    (1)判断非空白字符是否有效(即是否和转换说明要求相符,如要求%d却读到字符a),如果无效则读取立即停止,并将无效字符退回stdin中,如果有效则保存当前字符,然后继续从stdin中读取下个字符;
    (2)如果使用字段宽度修饰符,在有效字符数达到指定字段宽度或遇到第1个空白字符,两个条件任何一个满足则立即停止读取,如果是遇到空白字符停止,则将空白字符退出stdin中;
    (3)如果未用字段宽度修饰符,则在遇到第1个空白字符出停止,并将空白字符退出stdin中;
  3. 读取结束后对已保存的字符按照转换说明进行转换,然后储存在指定变量中;
  4. 继续读取下一个输入项(如果有的话)。

以scanf(“%d”, &num)为例
(1)要求从stdin中读取字符并转换为一个整数;
(2)有效字符为数字字符0-9或正负符号(±);
(3)每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始判断字符有效性;
(4)如果读到一个数字或符号,它便保存该字符,并读取下一个字符;
(5)不断地读取和保存字符,直至遇到非数字字符(注意空白也是非数字字符);如果遇到非数字字符,它便认为读到了整数的末尾,并把非数字字符放回stdin中(这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符);
(6)最后将已读取的字符转换为相应的数值,并储存在指定的变量num中;
(7)总结:scanf(“%d”,&num)完成从键盘读取有效字符、将字符转换成数值、将数值储存在变量num中的操作。

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

转换说明不同,有效字符也不同
(1)%x转换说明要求scanf()函数能识别十六进制数a-f和A-F;
(2)%f转换说明要求scanf()函数能识别小数点、e计数法和p计数法等;
(3)%s转换说明要求scanf()函数能识别除空白字符外的所有字符。

多个参数的情况
(1)scanf()函数使用空白字符把键盘输入分成多个字段;
(2)在依次把转换说明和字段匹配时跳过空白;
(3)只要在每个输入项之间输入至少一个空白字符即可,可以在一行或多行输入;
(4)唯一例外的时%c转换说明;根据%c,scanf()函数会读取每个字符,包括空白。

5 注意事项

(1)printf()函数的参数列表使用变量、常量和表达式;scanf()函数的参数列表使用指向变量的指针;
(2)对于float类型和double类型,printf()函数都使用%f转换说明;对于float类型,scanf()函数使用%f转换说明,对于double类型,scanf()函数使用%lf转换说明;
(3)格式字符串中的转换说明和参数列表在数量、顺序和类型上要完全匹配;
(4)sancf()函数的格式字符串和printf()函数一样由三部分组成:字符串字面量、转义序列和转换说明;但是,强烈建议scanf()函数的格式字符串不要包括字符串字面量和转义序列,对于键盘输入来说麻烦且没必要,只包含转换说明即可;
(5)如果想从键盘获取字符,用getchar()函数替代%c;
(6)如果想从键盘获取字符串,用gets()函数替代%s;scanf()函数搭配%s只能从键盘读取单词而不能读取句子;另外,scanf()函数在储存字符序列时,会在末尾加上字符串结束符(即空字符’\0’),组成一个字符串;
(7)使用scanf()函数之后一定要清空标准输入流stdin。

6 示例

6.1 scanf()函数读取过程中的丢弃和回退

代码如下图所示:

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中读取完毕;
第二阶段:
(6)gets()函数继续从标准输入流stdin中读取字符串;
(7)因标准输入流stdin中仍残留有scanf()函数未读完的字符,故无需用户再次从键盘键入字符;
(8)gets()函数会读空标准输入流stdin中的所有字符,直至遇到回车符’\n’;
第三阶段:
(9)printf()函数打印a和str的内容,并统计str的长度;a=12符合预期;str长度为3符合预期,即字符’a’+2个空格。

6.2 scanf()函数返回值

代码如下图所示:

void flush_stdin(void)
{
   char c_tmp;
   while (((c_tmp = getchar()) != '\n') && (c_tmp != EOF));
}

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);
   flush_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);
   flush_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);
   flush_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);
   flush_stdin();
   printf("a=%d, b=%d, c=%d, num=%d\n\n", a, b, c, num);

   return 0;
}

代码执行结果如下图所示:
在这里插入图片描述
分析程序及运行结果:
如第4节所述,scanf()函数在%d转换说明下,希望从标准输入流stdin中读取数字字符0-9和正负符号(±),当读取到非数字字符’a’时,将’a’退回至标准输入流stdin中,并停止读取。理论分析和num的实际打印结果相符合。

6.3 scanf()函数读取字符%c

代码如下图所示:

void flush_stdin(void)
{
   char c_tmp;
   while (((c_tmp = getchar()) != '\n') && (c_tmp != EOF));
}

int main()
{
   //变量定义
   char c1 = 0, c2 = 0, c3 = 0;
   //第1次输入
   puts("输入字符abc,中间不加空格 : ");
   scanf("%c%c%c", &c1, &c2, &c3);
   flush_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);
   flush_stdin();
   printf("c1=%c, c2=%c, c3=%c\n", c1, c2, c3);

   return 0;
}

代码执行结果如下图所示:
在这里插入图片描述
分析程序及运行结果:
如第4节所述,scanf()函数在%c转换说明下会读取键盘键入的每一个字符,包括空白字符。

6.4 scanf()函数读取字符串%s

代码如下图所示:

void flush_stdin(void)
{
   char c_tmp;
   while (((c_tmp = getchar()) != '\n') && (c_tmp != EOF));
}

int main()
{
   //变量定义
   char str[80] = { 0 };
   //
   puts("输入hello world");
   scanf("%s", str);
   flush_stdin();
   puts(str);

   return 0;
}

代码执行结果如下图所示:
在这里插入图片描述
分析程序及运行结果:
如第4节所述,scanf()函数在读取字符的时候,如果遇到空白字符,则停止读取,所以%s只能读取单词,不能读取句子。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值