C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 閱讀:68,249

C語言scanf函數用法完全攻略

本節介紹輸入函數 scanf 的用法。scanf 和 printf 一樣,非常重要,而且用得非常多,所以一定要掌握。

概述

scanf 的功能用一句話來概括就是“通過鍵盤給程序中的變量賦值”。該函數的原型為:

# include <stdio.h>
int scanf(const char *format, ...);

它有兩種用法,或者說有兩種格式。

1) scanf("輸入控制符", 輸入參數);

功能:將從鍵盤輸入的字符轉化為“輸入控制符”所規定格式的數據,然后存入以輸入參數的值為地址的變量中。

下面給大家舉個例子:
#include <stdio.h>
int main(void)
{
    int i;
    i = 10;
    printf("i = %d\n", i);
    return 0;
}
我們前面都是像這樣寫的,即直接給變量 i 賦一個值。但是這樣寫功能比較弱,因為這個值就變成一個“死值”了,它只能是 10,不可能是其他值,除非在程序中修改。很多時候我們希望這個值不是由程序員在程序中指定的,而是在程序運行的過程中由用戶從鍵盤輸入的。用戶輸入多少,變量i就是多少,這樣程序的功能就更加靈活了。

那么如何實現在程序運行的過程中由用戶從鍵盤輸出值呢?用 scanf 即可實現:
# include <stdio.h>
int main(void)
{
    int i;
    scanf("%d", &i);  //&i 表示變量 i 的地址,&是取地址符
    printf("i = %d\n", i);
    return 0;
}
“輸入控制符”和“輸出控制符”是一模一樣的。比如一個整型數據,通過 printf 輸出時用%d輸出,通過 scanf 輸入時同樣是用%d

要想將程序中的 scanf 行弄明白,首先要清楚的是:我們從鍵盤輸入的全部都是字符。比如從鍵盤輸入 123,它表示的并不是數字 123,而是字符 '1'、字符 '2' 和字符 '3'。這是為什么呢?

操作系統內核就是這樣運作的。操作系統在接收鍵盤數據時都將它當成字符來接收的。這時就需要用“輸入控制符”將它轉化一下。%d的含義就是要將從鍵盤輸入的這些合法的字符轉化成一個十進制數字。經過 %d 轉化完之后,字符 123 就是數字 123 了。

第二個要弄清楚的是:&是一個取地址運算符,&后面加變量名表示“該變量的地址”,所以&i就表示變量 i 的地址。&i又稱為“取地址i”,就相當于將數據存入以變量 i 的地址為地址的變量中。

那么以變量 i 的地址為地址的變量是哪個變量呢?就是變量 i。所以程序中 scanf 的結果就把值 123 放到變量i中。

綜上所述,scanf 語句的意思就是:從鍵盤上輸入字符 123,然后%d將這三個字符轉化成十進制數 123,最后通過“取地址 i”找到變量 i 的地址,再將數字 123 放到以變量 i 的地址為地址的變量中,即變量 i 中,所以最終的輸出結果就是i=123

注意,為什么不直接說“放到變量i中”?而是說“放到以變量 i 的地址為地址的變量中”?因為這么說雖然很繞口,但是能加強對 &i 的理解,這么說更能表達 &i 的本質和內涵。很多人在學習 scanf 的時候,經常將“變量 i”和“變量 i 的地址”混淆,從而思維開始混亂,等深刻了解 &i 的含義之后就可以不那么說了。

以上是 scanf 的最簡單用法,也是最常用、最基本、最重要的用法。這樣通過 scanf 就可以在程序運行的過程中由用戶來指定變量 i 的值,這與在程序中賦值相比較功能更強大。

2) scanf("輸入控制符非輸入控制符", 輸入參數);

這種用法幾乎是不用的,也建議你們永遠都不要用。但是經常有人問,為什么 printf 中可以有“非輸出控制符”,而 scanf 中就不可以有“非輸入控制符”。事實上不是不可以有,而是沒有必要!下面來看一個程序:
# include <stdio.h>
int main(void)
{
    int i;
    scanf("i = %d", &i);
    printf("i = %d\n", i);
    return 0;
}
在 printf 中,所有的“非輸出控制符”都要原樣輸出。同樣,在 scanf 中,所有的“非輸入控制符”都要原樣輸入。所以在輸入的時候i=必須要原樣輸入。比如要從鍵盤給變量 i 賦值 123,那么必須要輸入i=123才正確,少一個都不行,否則就是錯誤。

所以 scanf 中%d后面也沒有必要加\n,因為在 scanf 中\n不起換行的作用。它不但什么作用都沒有,你還要原樣將它輸入一遍。

所以在 scanf 的使用中一定要記住:雙引號內永遠都不要加“非輸入控制符”。除了“輸入控制符”之外,什么都不要加,否則就是自找麻煩。而且對于用戶而言,肯定是輸入越簡單越好。

一次給多個變量賦值:
# include <stdio.h>
int main(void)
{
    int i, j;
    scanf("%d%d", &i, &j);
    printf("i = %d, j = %d\n", i, j);
    return 0;
}
首先,scanf 中雙引號內除了“輸入控制符”之外不要加任何“非輸入控制符”。通過鍵盤給多個變量賦值與給一個變量賦值其實是一樣的。比如給兩個變量賦值就寫兩個 %d,然后“輸入參數”中對應寫上兩個“取地址變量”;給三個變量賦值就寫三個 %d,然后“輸入參數”中對應寫上三個“取地址變量”……

但是需要注意的是,雖然 scanf 中沒有加任何“非輸入控制符”,但是從鍵盤輸入數據時,給多個變量賦的值之間一定要用空格、回車或者 Tab 鍵隔開,用以區分是給不同變量賦的值。而且空格、回車或 Tab 鍵的數量不限,只要有就行。一般都使用一個空格。

此外強調一點:當用 scanf 從鍵盤給多個變量賦值時,scanf 中雙引號內多個“輸入控制符”之間千萬不要加逗號,

有些人覺得在輸入的時候可以用逗號分隔,所以就在“輸入控制符”之間用逗號隔開。這樣做從程序的角度確實是可以的,但是建議大家不要這樣做。在實際編程中這種寫法是絕對不允許的,原因有兩個:
  • 首先逗號要原樣輸入的,有幾個就要輸入幾個,少一個或多一個都不行;
  • 其次,也是最主要的原因就是輸入法的問題,在 scanf 中是在英文輸入法下寫的逗號,那么輸入的時候如果是中文輸入法下的逗號那也是錯的。所以用逗號很容易出錯。

最后再次強調:scanf“輸入參數”的取地址符&千萬不要忘了。這是初學者經常犯的錯誤。而 printf 中的“輸出參數”是不帶取地址符的,不要混淆了。

使用scanf的注意事項

1) 參數的個數一定要對應


在前面介紹 printf 時說過,“輸出控制符”和“輸出參數”無論在“順序上”還是在“個數上”一定要一一對應。這句話同樣對 scanf 有效,即“輸入控制符”和“輸入參數”無論在“順序上”還是在“個數上”一定要一一對應。比如:
# include <stdio.h>
int main(void)
{
    char ch;
    int i;
    scanf("%c%d", &ch);
    printf("ch = %c, i = %d\n", ch, i);
    return 0;
}
在 VC++ 6.0 中的輸出結果是:
a 6
ch = a, i = -858993460

這種錯誤是初學者經常犯的,由于粗心大意,少寫一個參數。更嚴重的是,這種錯誤在編譯的時候不會報錯。printf 也是一樣,即使“輸出參數”少寫了也不會報錯,但從程序的功能上講這么寫就是錯的。所以在編程的時候一定要避免這種錯誤的發生。

程序中為什么 i=–858993460?這個在《為什么要初始化變量》中講過,當變量沒有初始化的時候就會輸出這個值。

在后面會講到 scanf 是緩沖輸入的,也就是說從鍵盤輸入的數據都會先存放在內存中的一個緩沖區。只有按回車鍵后 scanf 才會進入這個緩沖區和取數據,所取數據的個數取決于 scanf 中“輸入參數”的個數。所以上述程序中 scanf 只有一個輸入參數,因此按回車鍵后 scanf 只會取一個數據。所以變量 ch 有數據,而變量 i 沒有數據,沒有數據就是沒有初始化,輸出就是 –858993460。

2) 輸入的數據類型一定要與所需要的數據類型一致

在 printf 中,“輸出控制符”的類型可以與數據的類型不一致,如:
# include <stdio.h>
int main(void)
{
    int i = 97;
    printf("i = %c\n", i);
    return 0;
}
在 VC++ 6.0 中的輸出結果是:
i = a

但是在 scanf 中,對于從鍵盤輸入的數據的類型、scanf 中“輸入控制符”的類型、變量所定義的類型,這三個類型一定要一致,否則就是錯的。雖然編譯的時候不會報錯,但從程序功能的角度講就是錯的,則無法實現我們需要的功能。比如:
# include <stdio.h>
int main(void)
{
    int i;
    scanf("%d", &i);
    printf("i = %d\n", i);
    return 0;
}
在 VC++ 6.0 中的輸出結果是:
a
i = -858993460

輸出 –858993460 表示變量未初始化。為什么輸入 a,變量 i 卻顯示未初始化呢?

在 scanf 中,從鍵盤輸入的一切數據,不管是數字、字母,還是空格、回車、Tab 等字符,都會被當作數據存入緩沖區。存儲的順序是先輸入的排前面,后輸入的依次往后排。按回車鍵的時候 scanf 開始進入緩沖區取數據,從前往后依次取。

但 scanf 中 %d 只識別“十進制整數”。對 %d 而言,空格、回車、Tab 鍵都是區分數據與數據的分隔符。當 scanf 進入緩沖區中取數據的時候,如果 %d 遇到空格、回車、Tab 鍵,那么它并不取用,而是跳過繼續往后取后面的數據,直到取到“十進制整數”為止。對于被跳過和取出的數據,系統會將它從緩沖區中釋放掉。未被跳過或取出的數據,系統會將它一直放在緩沖區中,直到下一個 scanf 來獲取。

但是如果 %d 遇到字母,那么它不會跳過也不會取用,而是直接從緩沖區跳出。所以上面這個程序,雖然 scanf 進入緩沖區了,但用戶輸入的是字母 a,所以它什么都沒取到就出來了,而變量 i 沒有值,即未初始化,所以輸出就是 –858993460。

但如果將 %d 換成 %c,那么任何數據都會被當作一個字符,不管是數字還是空格、回車、Tab 鍵它都會取回。

不但如此,前面講過,你從鍵盤輸入 123,這個不是數字 123,而是字符 '1'、字符 '2' 和字符 '3',它們依次排列在緩沖區中。因為每個字符變量 char 只能放一個字符。所以輸入“123”之后按回車,scanf 開始進入緩沖區,按照次序,先取字符 '1',如果還要取就再取字符 '2',以此類推。

如果都取完了還有 scanf 要取數據,那么用戶就需要再輸入。先寫一個程序看一下:
# include <stdio.h>
int main(void)
{
    char i, j, k;
    scanf("%c%c%c", &i, &j, &k);
    printf("i = %c, j = %c, k = %c\n", i, j, k);
    return 0;
}
在 VC++ 6.0 中的輸出結果是:
123
i = 1, j = 2, k = 3

從這個程序中我們看出,就單純地輸入 123,不加任何空格,按回車鍵之后就同我們所講的一樣,分別將字符 '1'、字符 '2' 和字符 '3' 賦給字符變量 i、j 和 k。

但是需要提醒大家注意的是,在之前程序中,因為 scanf 是 %d,所以 a 沒有被取出來,還在緩沖區中。當遇到下一個 scanf 是 %c 時它就會被取出來。但是如果一直沒有出現 %c,那么這時就會出現一個問題:scanf怎么取十進制整數?即使使用 %d,但是由于字符 a “擋”在最前面,scanf 進去先碰到的總是 a,也就無法取到它后面的整數,所以必須先將 a“弄走”。這就牽涉到“清空輸入緩沖區”的概念,這個稍后再講。

3) 在使用 scanf 之前使用 printf 提示輸入

大家想一想,前面寫的 scanf 程序有沒有不足的地方?

程序寫好之后,編譯、鏈接、執行,然后彈出黑窗口,出現一個光標在那不停地閃。對于編寫程序的人來說他知道要輸入什么,但是對于用戶而言,用戶怎么知道是什么意思呢?所以之前的程序都缺少提示信息!因此在使用scanf之前,最好先用printf提示用戶以什么樣的方式輸入,這樣可以大大提高代碼的質量。看看下面這個程序:
# include <stdio.h>
int main(void)
{
    int i, j;
    printf("請輸入兩個值,中間以空格分隔:");
    scanf("%d%d", &i, &j);
    printf("i = %d, j = %d\n", i, j);
    return 0;
}
這樣在執行的時候,用戶一看就知道是要輸入兩個值,然后中間用空格隔開。所以這樣寫就更人性化、智能化了。

小結

scanf 的使用看似細節繁雜,但使用起來非常簡單。就目前而言,只要掌握以下五點:
  1. 在 scanf 的“輸入參數”中,變量前面的取地址符&不要忘記。
  2. scanf 中雙引號內,除了“輸入控制符”外什么都不要寫。
  3. “輸出控制符”和“輸出參數”無論在“順序上”還是在“個數上”一定要一一對應。
  4. “輸入控制符”的類型和變量所定義的類型一定要一致。對于從鍵盤輸入的數據的類型,數據是用戶輸入的,程序員是無法決定的,所以在寫程序時要考慮容錯處理,這個稍后再講。
  5. 使用 scanf 之前先用 printf 提示輸入。

只要掌握了以上五點,scanf 的使用基本上就沒什么問題了。至于其他注意點,到后面講數組和指針的時候再介紹。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo