找到啦哈哈
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()函数释疑(下)
在上篇我已经表达了两个观点,这里再重申一次:1。本文仅对scanf()函数控制串运用进行探讨,本文所有例程并不构成编程建议。2。凡事要亲力而为,不同平台不同编译器,可能会有不同结果。本文所有例程均在WIN-TC+windows Me下调试。
四、 scanf()函数控制串运用出现的常见错误及对策技巧
问题一: 程序编译通过,但运行错误提示如下:
scanf : floating point formats not linked
Abnormal program termination
出错示例程序:
#include <stdio.h>
int main(void)
{
int i,j ;
float s[3][3];
/*这里*/
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",&s[i][j]);
for(i=0;i<3;i++)
for(j=0;j<3;j++)
printf("%f",s[i][j]);
}
这实际上是个与本文主题无关的问题,也是与scanf()函数无关,是编译器的问题。
原因很明确:没有链接浮点库。早期系统内存资源紧张,多维浮点数组占用内存量大(一维浮点数组就没有此问题),因此TC在编译时尽量不加入无关的部分,在没发现需要浮点转换程序时,就不在可执行程序中安装这个部分。而有时TC又不能正确识别实际上确实需要做浮点转换,因此就会出现上面错误。
解决的方法:告诉TC需要做浮点数的输入转换。将以下语句加入上面程序中标有/*这里*/处。
方法一: float c;
scanf("%f",&c);
方法二: float c,*t;//此处手误,现已更正&t===》*t;
t=&c;
.....
也就是说,编译器只要有浮点转换的线索,TC就会把浮点转换连上,所以一般大一点的程序里的就会有浮点变量反而没有此问题。
但问题到此并没结束,我在上面有句“一维浮点数组就没有此问题”,那么我们来看看这样行不行:
#include <stdio.h>
int main(void)
{
int i,j ;
float s[3][3],*ptr;
ptr=&s[0][0];
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",ptr+i*3+j);
for(i=0;i<3;i++)
for(j=0;j<3;j++)
printf("%7.2f\n",s[i][j]);
}
这样我们就把多维浮点数组降为一维浮点数组来处理,调试一下,程序运行正常。这说明TC编译器仅在处理多维浮点数组(结构体)有此“未链接浮点库”的问题。
类似问题:
#include "stdio.h"
#include "math.h"
main()
{ float a[3][3],sum=0;int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",&a[i][j]);
for(i=0;i<3;i++)
sum=sum+a[i][i];
printf("%f",sum);
getch();} 为什么结果一闪而过?该程序求矩阵对角元素之和
对于这个程序,由于出现了上面的问题,运行时系统提示:
scanf : floating point formats not linked
Abnormal program termination 在此之后系统就直接结束运行,所以屏幕一闪而过。
验证:
#include "stdio.h"
#include "math.h"
main()
{ float a[3][3],sum=0;int i,j;
sleep(7); /* 程序在此停留7秒钟,以判断运行位置*/
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",&a[i][j]);
for(i=0;i<3;i++)
sum=sum+a[i][i];
printf("%f",sum);
getch();}
然后再运行一次就可以看到上次运行时系统提示的 scanf : floating point formats not linked
Abnormal program termination
解决:在sleep( )语句之前增加如下内容:
float b;
printf("Input a float number :\n");
scanf("%f",&b);
用来告诉TC需要做浮点数的输入转换,问题就可以解决了
问题二: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()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数,scanf函数仅在每一个数据域均有数据,并按回车后结束)。
① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。
所以,上述程序并不能达到预期目的,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);
}
不得不说,fflush(stdin);是错的。fflush是用来清空标准输出的,fflush(stdin);在C语言中没有定义,是依赖编译器的,不可移植的。
an extract from the C standard will help explain:
int fflush(FILE *ostream);
ostream points to an output stream or an update stream in which the
most recent operation was not input, the fflush function causes any
unwritten data for that stream to be delivered to the host environment to
be written to the file; otherwise, the behavior is undefined.
On occasions you may need to clear unwanted data in an input stream, most commonly keyboard input. This may be after a call to a read function that failed to input all available data, or it may be to ensure that the user doesn't try the "type ahead" approach when using your application.
As far as standard C and C++ go, there is no guaranteed method to clear an input stream. You can write code that will do a reasonably good job, but it probably won't work in all instances your require it to. Why not? Because in standard C/C++ input streams are buffered. This means that when you hit a key on the keyboard, it isn't sent directly to your program. Instead it is buffered by the operating system until such time that it is given to your program. The most common event for triggering this input is the pressing of the [Enter] key.
If you are sure that unwanted data is in the input stream, you can use some of the following code snippets to remove them. However, if you call these when there is no data in the input stream, the program will wait until there is, which gives you undesirable results.
/*
* This C implementation will clear the input buffer.
* The chances are that the buffer will already be empty,
* so the program will wait until you press [Enter].
*/
#include <stdio.h>
int main(void)
{
int ch;
char buf[BUFSIZ];
puts("Flushing input");
while ((ch = getchar()) != '\n' && ch != EOF);
printf ("Enter some text: ");
if (fgets(buf, sizeof(buf), stdin))
{
printf ("You entered: %s", buf);
}
return 0;
}
/*
* Program output:
*
Flushing input
blah blah blah blah
Enter some text: hello there
You entered: hello there
*
*/
And a C++ version:
#include <iostream>
#include <cstdio>
using std::cin;
using std::cout;
using std::endl;
int main(void)
{
int ch;
char buf[BUFSIZ];
cout <<"Flushing input" <<endl;
while ((ch = cin.get()) != '\n' && ch != EOF);
cout <<"Enter some text: ";
cout.flush();
if (cin.getline(buf, sizeof(buf)))
{
cout <<"You entered: " <<buf <<endl;
}
return 0;
}
/*
* Program output:
*
Flushing input
blah blah blah blah
Enter some text: hello there
You entered: hello there
*
*/
But what about fflush(stdin);
Some people suggest that you use fflush(stdin) to clear unwanted data from an input buffer. This is incorrect.
Why fflush(stdin) is wrong
On occasions you may need to clear unwanted data in an input stream, most commonly keyboard input. One frequently suggested way of doing this is by using fflush(stdin). This is incorrect, and should be avoided, here is why.
stdin is a standard FILE* variable that points to the input stream normally used for keyboard input. The fflush() function is deemed to flush buffers. Put the two together and you have a method for clearing the input stream easily, right? WRONG! This is a common misconception in C and C++ programming, an extract from the C standard will help explain:
int fflush(FILE *ostream);
ostream points to an output stream or an update stream in which the
most recent operation was not input, the fflush function causes any
unwritten data for that stream to be delivered to the host environment to
be written to the file; otherwise, the behavior is undefined.
So, if the file stream is for input use, as stdin is, the behaviour is undefined, therefore it is not acceptable to use fflush() for clearing keyboard input.
As usual, there are some exceptions, check your compiler's documentation to see if it has a (non-portable) method for flushing input.
fflush(stdin);到底是什么呢:
有些人认为fflush(stdin)是用来清除在输入缓冲中你不想要的数据。但这样的认为是错的!
那fflush(stdin)为什么错呢?
有时你免不了需要清除一下那些在输入流中你不想要的数据,那些数据大多数情况是由键盘输入的。通常很习惯性的方法就是用fflush(stdin)来进行清除。但这样是错误的,而且应该避免,下面就是理由。(外国人怎么写文章比中国人还会托……烦死了)
stdin是一个标准FILE*(文件型指针)指向通常是用键盘的输入的输入流。fflush()的功能一般是被视为清除缓冲区的。一起输入两个,然后你有办法来清除这些输入流,不是么?错!这是一个对C/C++编程很菜鸟式的概念性错误认识!下面引用自C标准来帮助解释:
int fflush(FILE *ostream);
ostream流指向一个输出的流或一个在不久以前曾经操作过更(第四音)新的流,fflush的功能会导致任何并没有被写入缓冲流的数据被分配给主机环境,然后这些数据被写入文件;换句话,这种方式是没被定义的!(我狂晕啊,老外写的句子这么麻烦)
So,如果文件流是给输入用的,就像stdin一样,那么这种方式是没被定义的,因此用fflush()来清除键盘输入的这种方法是不能接受的。
通常,应该还有其他办法,查看一下你的协议文件是否有一种(非移植性--作者好像是想说非移植性指针)方法来清除输入。
老外的书,只能看其大概意思,具体一些鸟词,翻译不出来
或者说只可意会,不可言传---(一般菜鸟都这样说)