scanf()函數釋疑(上)ios
1、序言編程
scanf()函數的控制串的使用windows
例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;
}app
運行時按以下方式輸入三個值:ide
3□4□5 ↙(輸入a,b,c的值)函數
3,4,5 (printf輸出的a,b,c的值)ui
(1) &a、&b、&c中的&是地址運算符,分別得到這三個變量的內存地址。
(2) "%d%d%d"是按十進值格式輸入三個數值。輸入時,在兩個數據之間能夠用一個或多個空格、tab鍵、回車鍵分隔。
如下是合法輸入方式:
① 3□□4□□□□5↙
② 3↙
4□5↙
③ 3(tab鍵)4↙
5↙this
例2.spa
#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下調試。
4、 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);/*這裏%前沒有空格*/每次輸入一個字符,回車後,至關於多了一個字符,結果就是隻能輸 入5次!!!
}
}
使用了空格控制符後:
#include <stdio.h>
int main()
{
int i;
char j;
for(i = 0;i < 10;i++)
{
scanf(" %c",&j);//注意這裏%前有個空格!!!!!這裏僅是對於字符%c,若是是%d數字仍是輸入10次,///由於空格回車符,\n之類都是字符,因此用%c時出問題!
}
}
能夠運行看看兩個程序有什麼不一樣。
問題四如何處理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()來清除鍵盤輸入的這種方法是不能接受的。
一般,應該還有其餘辦法,查看一下你的協議文件是否有一種(非移植性--做者好像是想說非移植性指針)方法來清除輸入。