鱼C论坛

 找回密码
 立即注册
查看: 1326|回复: 1

[技术交流] 阻挡我们学习c的绊脚石(1)之scanf函数

[复制链接]
发表于 2018-12-30 22:49:52 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
        基于我在接触c语言的一些经历以及鱼c论坛中的一些萌新鱼油的问题,我还是想分享一下关于scanf输入函数的一些常见的应用技巧和常见问题,希望能给萌新鱼油一点启示,以起到抛砖引玉的作用。如有错误,还请各路大神勘误。
        我们在c中接触scanf非常早,常在学习c语言基本语法中使用它,来调试一些小程序。但是,使用它总是会出现莫名其妙的问题,导致小程序的输出完全跑偏。这就会给我们带来困扰,总会以为是程序的其他部分出现问题(因为scanf是人家给的呀,怎么会错呢),从而打击了新鱼油的自信。那么,今天我们就来看看,scanf它到底有多神秘。
        首先,来看一下 scanf的函数原型
  1. int scanf( const char *format [,argument]... );
复制代码

        msdn 给的功能介绍是从标准的输入流中读取格式化的数据。我们应该注意的就应该是什么是格式化数据,以及输入流是什么。 int类型的返回值,就是读取正确并且分配给对应变量的格式化数据个数或者是一个EOF标记,本贴暂不讨论文件相关,所以不探讨EOF。 第一个参数是一串格式串,就是我们常用的双引号里的东西,之后的参数叫做可变参数列表,就是我们熟悉的那些变量的地址了。
        然后,我们从函数的第一个参数开始看起。这是因为,这里直接决定了我们的scanf函数能否正确把数据分配给变量。下面是scanf支持的格式串。
       
c 如%c或%1c读入单字符,%2c读入两个字符(后面不会加上空字节),以此类推。
s 读入一个的字符序列,后面会加上空字节。
d, u 读入可选有符号(可选有符号表示输入时可以带符号也可以不带符号,不带符号则视为非负)十进制整数
i 读入可选有符号整数
a, e, f, g, A, E, F, G 读入可选有符号浮点数
o 读入可选有符号八进制整数
x, X 读入可选有符号十六进制整数
p 读入一个指针值
n 不读入任何字符,而是把到该位置已读入的字符数存储到与之对应的int*指向的位置。本转换说明符如果带有*或者带有域宽信息(如:%*n或%3n等),则后果是未定义的
% 读入% 符号(百分号)
* 表示读入的数据将被舍弃。带有*的格式指令不对应可变参数列表中的任何数据。可与其他格式混用 如 %*c

最值得我们注意的是,除以上的格式串之外,其他的格式串均会引起未定义的行为,也就是scanf函数不认识这些格式串,就不会从输入流中取出数据,那么数据就会停留在这个输入缓冲区内,等待下一个读数据的函数到来。(注:以上的格式串不必全部记住,只记住常用即可,但是要注意不是以上说明的格式串就是不合法的,不会被scanf认识。所以使用时不能乱编,如%.2f就是典型的乱写。)
        最后来看,可选参数列表,就是一堆指针,必须与前面的格式串一一对应。此处我们刚接触的时候会容易出错,把地址写成变量名,而熟悉之后并不容易出错。最值得注意的就是数组名是可以直接写的,因为数组名就是数组的地址。
        显然,scanf函数已经说完了。但是,我们的目的还没有达到,接着我们来说说一些错误和技巧。
        错误一:乱写格式串,就像之前说的%.2f就不可以。格式串中有域宽说明符,但是它是以一个非零的十进制整数形式出现。表示该格式指令最多读入的字符数。如
  1. char c[12];
  2. scanf("%5c", c);
  3. printf("%s", c);//输出结果只会是输入的前五位其余为乱码
  4. int i;
  5. scanf("%5d", &i);
  6. printf("%d", i);//输出只会是输入的前五位,少于5位按输入输出
复制代码

        错误二:把可变参数列表搞错,此处必须要是存储位置的指针。此处熟练之后基本不会出错。
        错误三:格式说明符之间的符号。格式串之间加空格,制表符、新行符会被忽略,因为这三者能用来分割数据项,而其他非空格字符则要求被匹配,说明如下。
  1. scanf("%d\n%d", &i, &j);
  2. printf("%d, %d", i, j);

  3. scanf("%d\t%d", &i, &j);
  4. printf("%d, %d", i, j);

  5. scanf("%d %d", &i, &j);
  6. printf("%d, %d", i, j);
复制代码

这三者等价
  1. scanf("%d\\%d", &i, &j);
  2. printf("%d, %d", i, j); // 此处就要求输入的是 如1\2
  3. scanf("%dt%d", &i, &j);
  4. printf("%d, %d", i, j); // 此处要求输入1t2

  5. scanf("%d,%d", &i, &j);
  6. printf("%d, %d", i, j);//此处就要求输入1,2

  7. scanf("%d%%%d", &i, &j);
  8. printf("%d, %d", i, j);  // 此处就要求输入1%2
复制代码

这样才能全部被输入。这就告诉我们,scanf不同于printf,格式转换符之间不能随便加符号。除非有特殊需要。
        再说一说常用的技巧。说是技巧有点太高估自己了,但是有自信毕竟是好事不是吗。(☆_☆)/~~
        技巧一:利用scanf函数的返回值。这个函数的返回值是经常被大家所忽略的,虽然小甲鱼的课程中涉及过。根据返回值,可以判断scanf函数是否按照我们的要求工作,直接上代码。
  1. int year, month, day, status;
  2.         printf("请按照年-月-日格式输入要算的生辰八字:");
  3.         status = scanf("%4d-%2d-%2d", &year, &month, &day);
  4.         if (3 == status)
  5.         {
  6.                 printf("%d-%d-%d", year, month, day);
  7.         }
  8.         else
  9.                 printf("您没按要求输入,失去了算卦的资格\n");
复制代码

这个例子,综合了我们之前说的一些知识点呢O(∩_∩)O。
        技巧二:利用*舍弃回车符 。scanf中处理字符出现的很多问题都是由回车符导致,因为我们确定数据时是按回车键,而恰巧scanf只读入对应的格式说明符对应字符,而回车键就留在缓冲区内,从而可能影响下面输入。如:
  1. char a,b;
  2. a = b = 'z';
  3. scanf("%c", &a);
  4. printf("i = %c", a);       
  5. scanf("%c", &b);
  6. printf("b = %c", b);//这给一个输入1给到a,回车,则输出为a = 1b= 换行
复制代码

而将 scanf("%c", &a);改为scanf("%c%*c"); 则结果不同
        技巧三:利用字符处理函数getchar()等函数,捕获回车。同上个问题。我们这样做。
  1. a = b = 'z';
  2. scanf("%c", &a);
  3. printf("i = %c", a);
  4. getchar();
  5. scanf("%c", &b);
  6. printf("b = %c", b);
复制代码

则也能让用户为b赋值。避免小错误。
        技巧四:暴力法(fflush函数),直接清除输入缓冲,这个就不知针对字符处理函数,可以用来调试,屏蔽模块之间的输入影响。如:
  1. a = b = 'z';
  2. scanf("%c", &a);
  3. printf("i = %c", a);
  4. fflush(stdin);
  5. scanf("%c", &b);
  6. printf("b = %c", b);
复制代码

注:fflush在vc6以上可能不可用;

好了。今天的篇幅也不小了,本来还想说一说缓冲区的前世今生,以及输入输出流和缓冲区的关系(本次是模糊了两者之间的概念的)。都放到下次的帖子来说吧。哇,第一次 发帖思维都有些混乱了。下期预告
getchar() 有缓冲区 stdio.h 有回显

getch() 无缓冲区 conio.h 无回显

getche() 无缓冲区 conio.h 有回显,是不是看着有点烦 。好了我们下次再见喽。拜拜n(*≧▽≦*)n

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-12-30 22:54:42 | 显示全部楼层
有大手子知道相应的更好的使用技巧,欢迎添加哦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-23 13:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表