函數scanf

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

概述

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

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

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

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

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

下面給大家舉個例子:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int i;
  5. i = 10;
  6. printf("i = %d\n", i);
  7. return 0;
  8. }

我們前面都是像這樣寫的,即直接給變量 i 賦一個值。但是這樣寫功能比較弱,因為這個值就變成一個“死值”了,它只能是 10,不可能是其他值,除非在程序中修改。很多時候我們希望這個值不是由程序員在程序中指定的,而是在程序運行的過程中由用戶從鍵盤輸入的。用戶輸入多少,變量i就是多少,這樣程序的功能就更加靈活了。

那么如何實現在程序運行的過程中由用戶從鍵盤輸出值呢?用 scanf 即可實現:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i;
  5. scanf("%d", &i); //&i 表示變量 i 的地址,&是取地址符
  6. printf("i = %d\n", i);
  7. return 0;
  8. }

“輸入控制符”和“輸出控制符”是一模一樣的。比如一個整型數據,通過 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 中就不可以有“非輸入控制符”。事實上不是不可以有,而是沒有必要!下面來看一個程序:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i;
  5. scanf("i = %d", &i);
  6. printf("i = %d\n", i);
  7. return 0;
  8. }

在 printf 中,所有的“非輸出控制符”都要原樣輸出。同樣,在 scanf 中,所有的“非輸入控制符”都要原樣輸入。所以在輸入的時候i=必須要原樣輸入。比如要從鍵盤給變量 i 賦值 123,那么必須要輸入i=123才正確,少一個都不行,否則就是錯誤。

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

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

一次給多個變量賦值:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i, j;
  5. scanf("%d%d", &i, &j);
  6. printf("i = %d, j = %d\n", i, j);
  7. return 0;
  8. }

首先,scanf 中雙引號內除了“輸入控制符”之外不要加任何“非輸入控制符”。通過鍵盤給多個變量賦值與給一個變量賦值其實是一樣的。比如給兩個變量賦值就寫兩個 %d,然后“輸入參數”中對應寫上兩個“取地址變量”;給三個變量賦值就寫三個 %d,然后“輸入參數”中對應寫上三個“取地址變量”……

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

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

有些人覺得在輸入的時候可以用逗號分隔,所以就在“輸入控制符”之間用逗號隔開。這樣做從程序的角度確實是可以的,但是建議大家不要這樣做。在實際編程中這種寫法是絕對不允許的,原因有兩個:

  • 首先逗號要原樣輸入的,有幾個就要輸入幾個,少一個或多一個都不行;
  • 其次,也是最主要的原因就是輸入法的問題,在 scanf 中是在英文輸入法下寫的逗號,那么輸入的時候如果是中文輸入法下的逗號那也是錯的。所以用逗號很容易出錯。


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

使用scanf的注意事項

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


在前面介紹 printf 時說過,“輸出控制符”和“輸出參數”無論在“順序上”還是在“個數上”一定要一一對應。這句話同樣對 scanf 有效,即“輸入控制符”和“輸入參數”無論在“順序上”還是在“個數上”一定要一一對應。比如:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. char ch;
  5. int i;
  6. scanf("%c%d", &ch);
  7. printf("ch = %c, i = %d\n", ch, i);
  8. return 0;
  9. }

在 VC++?6.0 中的輸出結果是:
a 6
ch = a, i = -858993460

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

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

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

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

在 printf 中,“輸出控制符”的類型可以與數據的類型不一致,如:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i = 97;
  5. printf("i = %c\n", i);
  6. return 0;
  7. }

在 VC++ 6.0 中的輸出結果是:
i = a

但是在 scanf 中,對于從鍵盤輸入的數據的類型、scanf 中“輸入控制符”的類型、變量所定義的類型,這三個類型一定要一致,否則就是錯的。雖然編譯的時候不會報錯,但從程序功能的角度講就是錯的,則無法實現我們需要的功能。比如:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i;
  5. scanf("%d", &i);
  6. printf("i = %d\n", i);
  7. return 0;
  8. }

在 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 要取數據,那么用戶就需要再輸入。先寫一個程序看一下:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. char i, j, k;
  5. scanf("%c%c%c", &i, &j, &k);
  6. printf("i = %c, j = %c, k = %c\n", i, j, k);
  7. return 0;
  8. }

在 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提示用戶以什么樣的方式輸入,這樣可以大大提高代碼的質量。看看下面這個程序:

  1. # include <stdio.h>
  2. int main(void)
  3. {
  4. int i, j;
  5. printf("請輸入兩個值,中間以空格分隔:");
  6. scanf("%d%d", &i, &j);
  7. printf("i = %d, j = %d\n", i, j);
  8. return 0;
  9. }

這樣在執行的時候,用戶一看就知道是要輸入兩個值,然后中間用空格隔開。所以這樣寫就更人性化、智能化了。

小結

scanf 的使用看似細節繁雜,但使用起來非常簡單。就目前而言,只要掌握以下五點:

  1. 在 scanf 的“輸入參數”中,變量前面的取地址符&不要忘記。
  2. scanf 中雙引號內,除了“輸入控制符”外什么都不要寫。
  3. “輸出控制符”和“輸出參數”無論在“順序上”還是在“個數上”一定要一一對應。
  4. “輸入控制符”的類型和變量所定義的類型一定要一致。對于從鍵盤輸入的數據的類型,數據是用戶輸入的,程序員是無法決定的,所以在寫程序時要考慮容錯處理,這個稍后再講。
  5. 使用 scanf 之前先用 printf 提示輸入。


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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374892.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374892.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374892.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言中定義變量位置

C標準的問題 C89規定&#xff0c;在任何執行語句之前&#xff0c;在塊的開頭聲明所有局部變量。 即應該如下&#xff1a;定義變量只能在最開始&#xff0c;scanf等執行語句之前 int N 0;double sum 0;scanf("%d",&N);在C99以及C中則沒有這個限制&#xff0c;即…

Java中的低GC:使用原語而不是包裝器

總覽 有兩個很好的理由在可能的地方使用原語而不是包裝器。 明晰。 通過使用原語&#xff0c;您可以清楚地知道null值是不合適的。 性能。 使用原語通常更快。 清晰度通常比性能更重要&#xff0c;并且是使用它們的最佳理由。 但是&#xff0c;本文討論了使用包裝程序對性能…

C# 連接Oracle數據庫以及一些簡單的操作

拖了很久今天終于在博客園寫了自己第一篇隨筆&#xff1a; 話不多說&#xff0c;我們直接進入正題&#xff1a; 1.連接數據庫 using (OracleConnection conn new OracleConnection("data source192.168.97.60/orcl;User Idabc;Passwordabc;")) { …

markdownb編輯器

這是H1 這是H2 這是H3 這是一個標題。 這是第一行列表項。這是第二行列表項。給出一些例子代碼&#xff1a; return shell_exec("echo $input | $markdown_script"); 轉載于:https://www.cnblogs.com/xcl461330197/p/4605163.html

Java Secret:使用枚舉構建狀態機

總覽 Java中的枚舉比許多其他語言更強大&#xff0c;這可能導致令人驚訝的用途。 在本文中&#xff0c;我概述了Java 枚舉的一些單獨功能&#xff0c;并將它們組合在一起形成一個狀態機。 單例和實用程序類的枚舉 您可以非常簡單地將枚舉用作Singleton或Utility。 enum Si…

數組部分筆記

對于數組的初始化需要注意以下幾點&#xff1a; 可以只給部分元素賦值。當{ }中值的個數少于元素個數時&#xff0c;只給前面部分元素賦值。例如&#xff1a; int a[10]{12, 19, 22 , 993, 344};表示只給 a[0]~a[4] 5個元素賦值&#xff0c;而后面 5 個元素自動初始化為 0。 …

指向函數的指針

指向函數的指針變量的一般形式為&#xff1a;數據類型 &#xff08;*指針變量名&#xff09;&#xff08;函數參數表列&#xff09;&#xff1b;如&#xff1a; int (*p)(int ,int );1、int (*p)(int ,int );表示定義一個指向函數的指針變量p&#xff0c;它不是固定只能指向…

核心Java面試答案不正確

總覽 在Internet上&#xff0c;Java面試問題和答案從一個網站復制到另一個網站。 這可能意味著錯誤或過時的答案可能永遠不會得到糾正。 這是一些不太正確或已經過時的問題和答案。 即是Java 5.0之前的版本。 每個提供的問題后都有兩個部分。 斜體的第一部分指示答案不完整/錯…

138.括號序列(區間型DP)

3657 括號序列 時間限制: 1 s空間限制: 256000 KB題目等級 : 黃金 Gold題解查看運行結果題目描述 Description我們用以下規則定義一個合法的括號序列&#xff1a; &#xff08;1&#xff09;空序列是合法的 &#xff08;2&#xff09;假如S是一個合法的序列&#xff0c;則 (S) …

C# 執行批處理文件(*.bat)的方法代碼

代碼如下:static void Main(string[] args){Process proc null;try{ string targetDir string.Format("D:\adapters\setup");//this is where mybatch.bat liesproc new Process();proc.StartInfo.WorkingDirectory targetDir;proc.StartInfo.Fil…

C語言空格怎么表示

1.直接敲空格就行&#xff0c;或者使用ASCII碼值賦值為32。 空格沒有轉義字符。 printf("12%c45 58",32);輸出 12 45 582.合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回車(CR)、\t 水平制表(HT)、\v 垂直制表(VT) 0、…

Tomcat中的零停機部署(和回滾); 演練和清單

親愛的大家&#xff0c; 如果您認為Tomcat不能再進步&#xff0c;那您就錯了。 Tomcat 7引入了所謂的并行部署 。 這是由SpringSource / VMWare貢獻的。 簡而言之&#xff0c;并行部署是一種能夠并行部署一個以上版本的Web應用程序的功能&#xff0c;使所有版本都可以在完全相…

javaweb 學習資源

http://jinnianshilongnian.iteye.com/category/231099轉載于:https://www.cnblogs.com/sishahu/p/5368018.html

HDU 1863 暢通工程(最小生成樹,prim)

題意&#xff1a; 給出圖的邊和點數&#xff0c;要求最小生成樹的代價&#xff0c;注&#xff1a;有些點之間是不可達的&#xff0c;也就是可能有多個連通圖。比如4個點&#xff0c;2條邊:1-2&#xff0c;3-4。 思路&#xff1a; 如果不能連通所有的點&#xff0c;就輸出‘?’…

2000年不算在21世紀

練習3-5 輸出閏年 (15 分) 輸出21世紀中截止某個年份以來的所有閏年年份。注意&#xff1a;閏年的判別條件是該年年份能被4整除但不能被100整除、或者能被400整除。 想當然地以為21世紀是2000~2099&#xff0c;當然沒有通過 if(N > 2000&&N < 2099){for(int i …

使用迭代器時如何避免ConcurrentModificationException

Java Collection類是快速失敗的&#xff0c;這意味著如果在使用迭代器遍歷某個線程的同時更改了Collection&#xff0c;則iterator.next&#xff08;&#xff09;將拋出ConcurrentModificationException 。 在多線程以及單線程環境下都可能出現這種情況。 讓我們通過以下示例探…

Sublime Text 3實用快捷鍵大全

下面是我通過網上教程和文本資料學習sublime Text3時收集的一些實用功能和常用快捷鍵&#xff0c;現在分享出來&#xff0c;如果還有其它的好用的功能可以在下面留言&#xff0c;以便互相學習和交流&#xff0c;謝謝&#xff01;。 選擇類 CtrlD 選中光標所占的文本&#xff0c…

Tomcat中配置JNDI數據源

準備工作&#xff1a; Tomcat版本&#xff1a;tomcat6.0以上 下例中均使用MySQL數據庫 將對應數據源的jar包和MySQL的驅動包拷貝至tomcat的lib文件夾下 一、全局數據源 1步驟一&#xff1a;配置 在tomcat下的conf/server.xml的GlobalNamingResources節點標簽中增加如下配置&…

練習3-8 查詢水果價格 (15 分)

練習3-8 查詢水果價格 (15 分) 給定四種水果&#xff0c;分別是蘋果&#xff08;apple&#xff09;、梨&#xff08;pear&#xff09;、桔子&#xff08;orange&#xff09;、葡萄&#xff08;grape&#xff09;&#xff0c;單價分別對應為3.00元/公斤、2.50元/公斤、4.10元/公…

JavaFX 2.0 beta示例應用程序和思考

我有一段時間回過頭來玩JavaFX&#xff0c;并且在使用該語言方面有好有壞的經驗。 隨著JavaFX 2.0 beta的發布&#xff0c;我想嘗試一下。 在這里&#xff0c;我開發了一個簡單的地址解析應用程序&#xff0c;該應用程序將使用Google地址編碼API來獲取地址并提供該位置的緯度-經…