网页
资讯
视频
图片
知道
文库
贴吧
地图
采购
进入贴吧
全吧搜索
吧内搜索
搜贴
搜人
进吧
搜标签
日
一
二
三
四
五
六
签到排名:今日本吧第
个签到,
本吧因你更精彩,明天继续来努力!
本吧签到人数:0
一键签到
成为超级会员,使用一键签到
一键签到
本月漏签
0
次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行
补签
。
连续签到:
天 累计签到:
天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
12月02日
漏签
0
天
c语言吧
关注:
798,058
贴子:
4,340,106
看贴
图片
吧主推荐
视频
游戏
1
2
3
下一页
尾页
92
回复贴,共
3
页
,跳到
页
确定
<<返回c语言吧
>0< 加载中...
关于C语言scanf的一点讲解
只看楼主
收藏
回复
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
有些人不喜欢自己读说明书,那我给他们讲解一下好了
风音观铃
团子家族
10
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
好人一生平安~
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
scanf 字面意思 scan+f(ormat) 按格式扫描
用户的输入在某种奇怪的设计哲学里是当作文件处理的,或者对应 C++ 里的流——就是一个可以单方向读入的(字符)串,读入以后再也没法回头读(有例外)。
在入门教材、学校/培训机构课程中的练习、示例当中涉及到交互的时候,经常会说 scanf 是用来“输入一个整数,输入一个字符串”的东西,仿佛能直接输入对应类型的对象,仿佛两个对象的输入之间是完全分开的。
其实实际的机制不那么直观,很多入门教材、课程以入门介绍为目的不会好好讲解 scanf 的原理和用法(也没有那个必要)。但是很多入门学习的人就是会在这些地方踩到陷阱,或者就是喜欢在这种地方钻牛角尖。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
scanf的第一个参数是格式参数,入门教材里会说 "%d" 就会读入一个整数,然后赋值给对应位置参数指针指向的对象。scanf("%d",ptr); 效果就行是 *ptr=getInt(); (如果真是这样该多好,省得解释这么多了)
事实上,scanf 把你的输入当成一个字符串处理。你输入的时候大致会这样操作——输入几位数字,然后按下回车确认,这时候命令行换了一行,程序从 scanf 的地方继续向下执行。
假设你想输入的数是42,你的输入实际上是"42\n"。scanf从第一个参数(格式字符串参数)里读到了%d,就知道应该从你的输入字符串里最开头读取一个整数(然后以int形式保存)。具体过程是这样的——读入'4',是一个数字,继续读下一个字符'2',还是数字,继续读'\n',不是数字,于是数的输入就结束了,'\n'被留在输入的字符串里,'4'和'2'已经被读过,后面再也不可能回来读了。然后读出来的数字'4'和'2'经过计算表示十进制数42,写入对应位置参数指针指向的对象。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
所有 scanf 调用都操作的是同一个输入文件(字符串),前一个 scanf 舍弃掉的字符,后一个就读不到了,每一个 scanf 都从上一次 scanf 读到的进度继续阅读输入的字符串。
这个例子结束为止一切和入门教程上说的没有什么区别。
但事实上有一个矛盾——输入应该是一个字符串,你有接连的几个scanf,中间还夹着计算、输出:
例1.
int a,b;
scanf("%d",&a);\\类似*(&a)=getInt();
printf("a=%d\n",a);
scanf("%d",&b);\\类似*(&b)=getInt();
printf("b=%d\n",b);
命令行当中看起来像这样
12
a=12
450
b=12450
用户的输入即使在按了回车以后还是没有结束的(实际上只有输入特殊字符才能结束用户输入)
这个例子用教科书里的说法(模型)可以解释的通,但是用这里说的“scanf 阅读字符串”就说不通——输入都还没有完成(没有整个字符串都输入完),输出就先蹦出来了
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
如果按照先前的解释,输入输出应该像这个样子:
12
450
^z
a=12
b=450
其中^z就是我说的结束输入的特殊字符,输入方式是ctrl+z;
回到文件的说法,用户的输入是一个单向读取的文件,与之相对,输入过程中用户就相当于在写入这个文件。如果用户一次性写完再让程序读取,就不可能进行“交互”,在输入以后立马看到结果,再根据看到的结果决定下一步输入什么。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
而你就可以根据程序对你写的小说的反馈,决定接下来写什么内容。
例2.
//学生管理系统的片段,程序只用来说明,请不要模仿这种写法
printf("please input the operation:");
scanf("%s",opBuff);//读取要进行的操作
if(!strcmp(opBuff,"add")){//添加学生
printf("please input id, name, age:");
scanf("%d %s %d",&id,name,&age);//读取要进行的操作
student_record sr;
make_student_record(&sr,id,name,age);
if(insert(&sr)) printf("insert succeeded");
else printf("insert failed");
}else if(...){//其他操作
}
...
涓婂笣嗫嬩箣鐪
超能力者
9
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
要时刻记着,scanf 只能从你输入的字符串里推测你输入的哪一段对应他的哪一个格式说明符,如果有两种以上的对应方法,就会按前面所说的“最长匹配”的原则来匹配。abcde如果可以切分成abc/de ab/cde两种切分方法都符合,scanf 一定会选择abc/de。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
有了以上的知识以后再讲讲怎么处理输入错误——
scanf 是有返回值的。在输入没有正确进行的时候,scanf 会返回比格式说明符数量少的整数,也即成功输入的格式说明符的数量,比如scanf("%d%d%d"),如果在读到第三个%d时出错了,scanf 会中止,并返回2。并且,错误输入的部分就留着不会继续读下去。
下一次 scanf ,还会从上一个 scanf 放弃的地方开始。
这次你扮演读者,如果是读取真正一次性写好一次性读取的文件,出了错误是不可能纠正的,但是交互程序就可以——你可以要求用户重新输入之前输入错误的部分。
问题是,你不知道从哪里开始是用户重新输入的部分。怎么跳过错误的部分从重新输入的部分开始读起呢?
有些书会教你 fflush(STDIN); 然而这是 UB(请自行搜索)。你要做的是,一直读取,直到发现上一次失败 scanf 应该结束的位置。
其实可以简单做个推理,scanf 能继续执行,一定是输入了回车。在用户看到“输入错误”的提示之前他一定刚刚输入了回车,在继续输入之前一定来得及看到“输入错误”的提示。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
对于 scanf 来说,他是没有时间概念的
错例2.
while(scanf("%d",&a)<1){
scanf(magic);//假设magic可以匹配任意字符而且丢弃(实际上并不存在)
printf("input error, please input an integer:");
}
对于 scanf 来说,他是没有时间概念的,丢弃错误输入的scanf(magic);不知道到底该什么时候停,他不知道自己被调用之前用户实际上输入了多少——他丢完了已有的输入以后,还会继续要求输入,然后把你的输入再丢掉,读完又问你要,周而复始。
wxd356
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
正确的实现是
例5.
while(scanf("%d",&a)<1){
scanf("%*[^\n]");
printf("input error, please input an integer:");
}
你可以当成一个模板,写的时候类似这样:
while(scanf(...)<n){
scanf("%*[^\n]");
printf("input error, please input (what you specified up there):");
}
scanf("%*[^\n]"); 的意思是读取任意不是换行的字符,并且丢弃不保存,有兴趣的话可以看 cppref 的解释。
642985327
彩虹面包
13
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
好人一生平安
HelloMrWoo
麻婆豆腐
11
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
睡前留名
保登心爱
彩虹面包
13
该楼层疑似违规已被系统折叠
隐藏此楼
查看此楼
留名
______我才不是水13呢___w4__我只是挽尊党啦
登录百度账号
扫二维码下载贴吧客户端
下载贴吧APP
看高清直播、视频!
贴吧页面意见反馈
违规贴吧举报反馈通道
贴吧违规信息处理公示