原文地址:https://blog.csdn.net/21aspnet/article/details/174326

scanf

函数名: scanf
功 能: 执行格式化输入
用 法: int scanf(char *format[,argument,…]);

scanf()函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。
调用格式为: scanf("<格式化字符串>",<地址表>);
scanf()函数返回成功赋值的数据项数,出错时则返回EOF(-1)。

其控制串由三类字符构成:

  • 格式化说明符;
  • 空白符;
  • 非空白符;

(A) 格式化说明符

格式字符 说明
%a 读入一个浮点值(仅C99有效)
%A 同上
%c 读入一个字符
%d 读入十进制整数
%i 读入十进制,八进制,十六进制整数
%o 读入八进制整数
%x 读入十六进制整数
%X 同上
%s 读入一个字符串
%f 读入一个浮点数 float
%lf 读入一个浮点数 double
%F 同上
%p 读入一个指针
%u 读入一个无符号十进制整数
%[] 扫描字符集合

附加格式说明字符表

修饰符 说明
L/l 长度修饰符 输入"长"数据
h 长度修饰符 输入"短"数据
W 整型常数 指定输入数据所占宽度
* 星号 空读一个数据

(B) 空白字符
空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符,空白符可以是space,tab,newline等等,直到第一个非空白符出现为止。
(C) 非空白字符
一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。

注:scanf()控制串知识就介绍到这里(应该比较齐全了_),如有遗漏下次补上。下面将结合实际例程,一一阐述.

三、 scanf()函数的控制串的使用

例1. 输入方式

#include "stdio.h"
int main(void)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d,%d,%d\n",a,b,c);
return 0;
}

运行时按如下方式输入三个值:

3□4□5 ↙//(输入a,b,c的值)
3,4,5 //(printf输出的a,b,c的值)

(1) &a、&b、&c中的&是地址运算符,分别获得这三个变量的内存地址。
(2) "%d%d%d"是按十进值格式输入三个数值。输入时,在两个数据之间可以用一个或多个空格、tab键、回车键分隔。
以下是合法输入方式:
① 3□□4□□□□5↙
② 3↙
4□5↙
③ 3(tab键)4↙
5

例2.

#include "stdio.h"
int main(void)
{
int a,b,c;
scanf("%d,%d,%d",&a,&b,&c);
printf("%d,%d,%d/n",a,b,c);
return 0;
}

运行时按如下方式输入三个值:

  • 3,4,5 ↙(输入a,b,c的值)
  • 或者 3,□4,□5 ↙(输入a,b,c的值)
  • 3,□□□4,□5 ↙(输入a,b,c的值)
    …
    都是合法的,但是","一定要跟在数字后面,如:
    3□,4,□5 ↙就非法了,程序出错。(解决方法与原因后面讲)
    再如:
    1、sacnf()中的变量必须使用地址。
int a, b;
scanf("%d%d",a,b); //错误
scanf("%d%d",&a,&b);

2、scanf()的格式控制串可以使用其它非空白字符,但在输入时必须输入这些字符。
例:

scanf("%d,%d",&a,&b);

输入: 3,4 ↙(逗号与"%d,%d"中的逗号对应)

scanf("a=%d,b=%d",&a,&b);

输入: a=3,b=4 ↙(“a=”,“b=”,逗号与"%d,%d"中的"a=","b="及逗号对应)

3、在用"%c"输入时,空格和“转义字符”均作为有效字符。
例:

scanf("%c%c%c",&c1,&c2,&c3);

输入:a□b□c↙
结果:a→c1,□→c2,b→c3 (其余被丢弃)

scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:
(不是结束该scanf函数,scanf函数仅在每一个数据域均有数据,并按回车后结束)。

  • 遇空格、“回车”、“跳格”键。
  • 遇宽度结束。
  • 遇非法输入。

问题二:scanf()函数不能正确接受有空格的字符串?如: I love you!

#include <stdio.h>
int main()
{
char str[80];
scanf("%s",str);
printf("%s",str);
return 0;
}

输入:I live you!
输出:I

所以,上述程序并不能达到预期目的,scanf()扫描到"I"后面的空格就认为对str的赋值结束,并忽略后面的"love you!".这里要注意是"love you!"还在键盘缓冲区(关于这个问题,网上我所见的说法都是如此,但是,我经过调试发现,其实这时缓冲区字符串首尾指针已经相等了,也就是说缓冲区清空了,scanf()函数应该只是扫描stdin流,这个残存信息是在stdin中)。我们改动一下上面的程序来验证一下:

#include <stdio.h>
int main()
{
char str[80];
char str1[80];
char str2[80]; scanf("%s",str);/*此处输入:I love you! */
printf("%s",str);
sleep(5);/*这里等待5秒,告诉你程序运行到什么地方*/
scanf("%s",str1);/*这两句无需你再输入,是对键盘盘缓冲区再扫描 */
scanf("%s",str2);/*这两句无需你再输入,是对键盘盘缓冲区再扫描 */
printf("\n%s",str1);
printf("\n%s",str2);
return 0;
}

输入:I love you!
输出:I
love
you!
好了,原因知道了,那么scanf()函数能不能完成这个任务?
回答是:能!别忘了scanf()函数还有一个 %[] 格式控制符(如果对%[]不了解的请查看本文的上篇),请看下面的程序:

#include "stdio.h"
int main()
{
char string[50]; /*scanf("%s",string);不能接收空格符*/
scanf("%[^/n]",string);
printf("%s/n",string);
return 0;
}

问题三:键盘缓冲区残余信息问题

#include <stdio.h>
int main()
{
int a;
char c;
do
{
scanf("%d",&a);
scanf("%c",&c);
printf("a=%d c=%c/n",a,c);
/*printf("c=%d/n",c);*/
}while(c!='N');
}

scanf("%c",&c);这句不能正常接收字符,什么原因呢?我们用printf(“c=%d/n”,c);将C用int表示出来,启用printf(“c=%d/n”,c);这一句,看看scanf()函数赋给C到底是什么,结果是 c=10 ,ASCII值为10是什么?换行即/n.对了,我们每击打一下"Enter"键**,向键盘缓冲区发去一个“回车”(/r),一个“换行"(/n),**在这里/r被scanf()函数处理掉了(姑且这么认为吧_),而/n被scanf()函数“错误”地赋给了c.
解决办法:可以在两个scanf()函数之后加个fflush(stdin);,还有加getch(); getchar();也可以,但是要视具体scanf()语句加那个,这里就不分析了,读者自己去摸索吧。但是加fflush(stdin);不管什么情况都可行。
函数名: fflush
功 能: 清除一个流
用 法: int fflush(FILE *stream);

#include <stdio.h>
int main()
{
int a;
char c;
do
{
scanf("%d",&a);
fflush(stdin);
scanf("%c",&c);
fflush(stdin);
printf("a=%d c=%c/n",a,c);
}while(c!='N');
}

这里再给一个用“空格符”来处理缓冲区残余信息的示例:
运行出错的程序:

#include <stdio.h>
int main()
{
int i;
char j;
for(i = 0;i < 10;i++)
{
scanf("%c",&j);/*这里%前没有空格*/
}
}

使用了空格控制符后:

#include <stdio.h>
int main()
{
int i;
char j;
for(i = 0;i < 10;i++)
{
scanf(" %c",&j);/*注意这里%前有个空格*/
}
}
可以运行看看两个程序有什么不同。

问题四 如何处理scanf()函数误输入造成程序死锁或出错?

#include <stdio.h>
int main()
{
int a,b,c; /*计算a+b*/
scanf("%d,%d",&a,&b);
c=a+b;
printf("%d+%d=%d",a,b,c);
}

如上程序,如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个错误的结果,呵呵,这可能所有人都遇到过的问题吧?
解决方法:scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题。
正确的例程:

#include <stdio.h>
int main()
{
int a,b,c; /*计算a+b*/
while(scanf("%d,%d",&a,&b)!=2)fflush(stdin);
c=a+b;
printf("%d+%d=%d",a,b,c);
}

scanf函数探讨

1.空白符问题

#include <stdio.h>
main()
{
int a;
printf( "input the data/n ");
scanf( "%d/n ",&a);//这里多了一个回车符/n
printf( "%d ",a);
return 0;
}

结果要输入两个数程序才结束,而不是预期的一个。why?
原因:用空白符结尾时,scanf会跳过空白符去读下一个字符,所以你必须再输入一个数。这里的空白符包括

空格,制表符,换行符,回车符和换页符。所以如果你用scanf( "%d ",&a)也会出现同样的问题。
解决方法:这种错误大多是输入的时候不小心,多注意一点就好了。这种问题也不好检查,编译没有问题,

一个空格也不容易看出来。当你的程序出现上面的问题时,自己对照检查一下就可以了。

2.缓冲区问题
这是一个非常容易错的地方,我就错过多次。

#include <stdio.h>
main()
{
int n = 5;
char c[n];
for(int i = 0; i < n; i++)
c[i] = scanf( "%c ",&c[i]);
printf(c);
return 0;
}

如果输入:
a
b
c
那么循环就会“提前”结束了.
原因:输入a和第一个回车后,a和这个回车符都留在缓冲区中。第一个scanf读取了a,但是输入缓冲区里面

还留有一个/n,第二个scanf读取这个/n。然后输入b和第二个回车,同样的,第三个scanf读取了b,第四个

scanf读取了第二个回车符。第五个读取了c。所以五个scanf都执行了,并没有提前结束。只不过有的scanf

读取到了回车符而已。
解决方法:把程序改成这样就可以了:

for( i = 0; i < n; i++){
scanf( "%c ",&c[i]);
fflush(stdin);//刷新缓冲区
}

或者不用scanf,而用gets()函数,如:

#include <stdio.h>
main()
{
char c[5];
gets(c);
printf(c);
return 0;
}

但要注意:这个函数自动把你最后敲的回车转换为字符 '/0 '。如果你的输入超过了数组的大小,那么就会产

生错误。

3.scanf()函数的参数输入类型不匹配问题
这是我在csdn论坛上见到的问题,这个错误有时候会让人莫名其妙。

#include <stdio.h>
main()
{
int a=123;
char c= 't ';
printf( "input/n ");
scanf( "%d%c ",&a,&c);
scanf( "%d%c ",&a,&c);
scanf( "%d%c ",&a,&c);
printf( "%d/n%c/n ",a,c);
return 0;
}

当输入a 回车 后,会直接跳过下面2个scanf语句,直接输出为
123
t
原因:对于scanf( "%d%c ",&a,&c),scanf语句执行时,首先试图从缓冲区中读入一个%d类型的数据,如果和

第一个参数匹配,则继续从缓冲区中读取数据和第二个参数进行匹配,依次进行下去,直到匹配完所有的参

数;如果其中有一个参数不匹配,那就从这个地方跳出,忽略这个scanf后面所有的参数,而去执行下一条语

句。
可以用下面的程序验证一下:

#include <stdio.h>
int main()
{
int a=123,b=1;
char c= 't ';
scanf( "%d%d ",&a,&b);
scanf( "%c ",&c);
printf( "%d/n%d/n%c/n ",a,b,c);
return 0;
}

输入:2 回车a 回车
结果是:
2
1
a
解决方法:scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个scanf()函数有几个变量

,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘缓

冲区就可能还个有残余信息问题。
比如:

#include <stdio.h>
main()
{
int a=123,b;
while(scanf( "%d%d ",&a,&b)!=2)
fflush(stdin);
printf( "%d/n%d/n ",a,b);
return 0;
}

你可以试一下,如果输入不是数字时,会有什么反应。

补充:scanf中一种很少见但很有用的转换字符:[…]和[ ^…]。

#include <stdio.h>
main()
{
char strings[100];
scanf( "%[1234567890] ",strings);
printf( "%s ",strings);
return 0;
}

运行,输入:1234werew后,结果是:1234。
通过运行可以发现它的作用是:如果输入的字符属于方括号内字符串中某个字符,那么就提取该字符;如果

一经发现不属于就结束提取。该方法会自动加上一个字符串结束符到已经提取的字符后面。
scanf( "%[^1234567890] ",strings); 它的作用是:如果一经发现输入的字符属于方括号内字符串中某个字符

,那么就结束提取;如果不属于就提取该字符。该方法会自动加上一个字符串结束符到已经提取的字符后面

。
注意:方括号两边不能空格,如:scanf( "%[ 1234567890 ] ",strings); scanf( "%[ ^1234567890

] ",strings); 不让空格也会算在里面的。
用这种方法还可以解决scanf的输入中不能有空格的问题。只要用
scanf( "%[^/n] ",strings); 就可以了。很神奇吧。

C语言scanf函数详细解释(转载)的更多相关文章

  1. C语言scanf函数详细解释

    原文链接 函数名: scanf 功 能: 执行格式化输入 用 法: int scanf(char *format[,argument,...]); scanf()函数是通用终端格式化输入函数,它从标准 ...

  2. [转载]VS2012编译C语言scanf函数error的解决方法

    在VS 2012 中编译 C 语言项目,如果使用了 scanf 函数,编译时便会提示如下错误: error C4996: 'scanf': This function or variable may ...

  3. C语言scanf函数详解

    函数名: scanf  功 能: 运行格式化输入  用 法: int scanf(char *format[,argument,...]); scanf()函数是通用终端格式化输入函数,它从标准输入设 ...

  4. C语言Scanf函数

    C语言的scanf函数 一.变量的内存分析 (一)字节与地址 ①. 内存以字节为单位 每个字节都有自己的内存地址,根据地址就可以找到该字节.整个内存相当于一整个酒店,而酒店以房间为单位,在这里每个房间 ...

  5. scanf函数具体解释与缓冲区

    1.基本信息 函数原型: int scanf( char *format, args, ...); 函数返回值: 读入并赋给args的数据个数.遇到文件结束返回EOF,出错返回0. 函数功能: sca ...

  6. C语言scanf函数转换说明表及其修饰符表

    1. 对于上一篇文章,总结printf()输出,C库也包含了多个输入函数, scanf()是最常用的一个,也是经常与printf()经常一起搭配使用的函数之一. scanf()和printf()类似, ...

  7. 对单片机的modbus RTU的详细解释(转载)

    Modbus 一个工业上常用的通讯协议.一种通讯约定.Modbus协议包括RTU.ASCII.TCP.其中MODBUS-RTU最常用,比较简单,在单片机上很容易实现.虽然RTU比较简单,但是看协议资料 ...

  8. C语言printf()函数具体解释和安全隐患

    一.问题描写叙述 二.进一步说明 请细致注意看,有例如以下奇怪的现象 int a=5; floatx=a; //这里转换是没有问题的.%f打印x是 5.000000 printf("%d\n ...

  9. C语言scanf函数返回值小记

    scanf scanf是C标准库stdio里面定义的用于获取用户输入的函数,具体的介绍可以在CppReference上看到.scanf的返回值是已经成功赋值的变量个数,也就是说在 scanf(&quo ...

随机推荐

  1. 第五课 Struts的控制器【续】Action类的execute()方法

    1.Action类的execute()方法: public ActionForward execute(ActionMapping mapping,                           ...

  2. SIM卡APDU指令【转】

    本文转载自:http://blog.csdn.net/hlx156/article/details/54136756 一个APDU可以是一个命令,也可以是命令的响应. 命令APDU的一般格式: CLA ...

  3. 转 source insight 复制后光标在前面

    source insight 里编辑的时候,每次粘贴后,光标停留在粘贴内容的前面. 我想把它设定为 粘贴后,光标移动倒粘贴内容的后面. 怎么做? 这是个设置问题,按照下面的步骤设定就可以了. Opti ...

  4. POJ2503 Babelfish map或者hash_map

    POJ2503 这是一道水题,用Map轻松AC. 不过,可以拿来测一下字符串散列, 毕竟,很多情况下map无法解决的映射问题需要用到字符串散列. 自己生成一个质数, 随便搞一下. #include&l ...

  5. springboot根据yml配置文件选择性加载bean

    @Slf4j @Aspect @Component @ConditionalOnProperty(value = "localCache.apiCache", havingValu ...

  6. P1128 [HNOI2001]求正整数

    传送门 rqy是我们的红太阳没有它我们就会死 可以考虑dp,设\(dp[i][j]\)表示只包含前\(j\)个质数的数中,因子个数为\(i\)的数的最小值是多少,那么有转移方程 \[f[i][j]=m ...

  7. java io 读取写文件

    java 读取txt文件,汉字乱码,原因是因为文件的编码格式和程序编码采用了不同的编码格式.通常,假如自己不修改的话,windows自身采用的编码格式是gbk(而gbk和gb2312基本上是一样的编码 ...

  8. E - Easy Dijkstra Problem(求最短路)

    Description Determine the shortest path between the specified vertices in the graph given in the inp ...

  9. Spring Boot (27) actuator服务监控与管理

    actuaotr是spring boot项目中非常强大的一个功能,有助于对应用程序进行监控和管理,通过restful api请求来监管.审计.收集应用的运行情况,针对微服务而言它是必不可少的一个环节. ...

  10. RS485通信和Modbus协议(转)

    转自:http://www.51hei.com/bbs/dpj-23230-1.html 在工业控制.电力通讯.智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换.最初采用的方式是RS232接 ...