scanf
首先scanf會先去緩衝區中讀取資料,倘若沒有資料則會等待使用者輸入資料,並利用前面講過的輸出控制字元,將使用者輸入的字轉為指定的類型,找到負責接收的變數位置,賦予該值。聽不懂也沒關係,我們先來看程式碼。
#include "stdio.h"
int main(void){
int a;
scanf("%d",&a);//%d 是整數,所以後面的變數也要是整數類型喔!
printf("這是使用者輸入的值: %d",a);
}
--------------------------------結果---------------------------------
123//我輸入的值
這是使用者輸入的值: 123
在scanf當中,我們在""中間所使用的輸出控制字元來表示我們希望的變數類型。當我輸入123之後,電腦就會立刻偵測這個123並轉化為整數型態,也就是%d。接著把123這個值,給予我所指定的 a 變數。(剛剛提到的緩衝區,我們留到後面在說)
至於 a 前面的&符號,則是表示顯示出 a 變數在記憶體中的位置,這樣子電腦就能決定好值的類型後,直接透過 &a 從記憶體找出 a 這個變數,最後再給它123的值。
注意!
△千萬不要在scanf的""當中,亂寫一些非輸出控制字元的字。
比方說:scanf("這是使用者輸入的值: %d",&a),這樣是不會讓 a 在輸出的時候,就自帶這些字的喔!反而還會讓使用者在輸入字的時候,要跟上面的一模一樣才不會出錯,這樣多此一舉不如只寫有意義的%d就好。
△區隔問題
倘若今天要輸入多個變數時,我們僅需要以","做為區隔即可,但在""當中的輸出控制字元,就千萬不要用","隔開唷!你可以使用空格做為美觀的區隔,但用逗號的話,只會導致使用者在輸入時也要強迫要用逗號區隔。如以下程式碼:
#include "stdio.h"
int main(void){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("這是使用者輸入的值: %d %d %d",a,b,c);
}
--------------------------------結果---------------------------------
1 4 5
這是使用者輸入的值: 1 4 5
△使用者輸入
在要求需要輸入多個變數的情況下,使用者僅需要以空格或是輸入、回車鍵(enter keyboard)、水平製表、跳格鍵(tab keyboard)作為變數間的區隔即可。如:
(以上方程式碼作為例子)
--------------------------------結果---------------------------------
1 4 5
這是使用者輸入的值: 1 4 5
--------------------------------或是---------------------------------
1
4
5
這是使用者輸入的值: 1 4 5
△緩衝區問題
這就得更深的講到scanf的原理了,首先scanf並不是直接把我們輸入的值給予變數,而是會先去搜尋緩衝區的資料。就有點像是我們輸入字之後,它會先跑到一個叫做緩衝區的地方待著,一旦程式碼遇到scanf就會來到這個緩衝區找資料。如果緩衝區沒有資料的時候,才會等待使用者輸入新的資料。乍看之下好像沒甚麼問題,但請看看這段程式碼:
# include <stdio.h>
int main(void){
int a;
char b;//字元,儲存ascii小於128的字
printf("請輸入一個整數: ");
scanf("%d",&a);
printf("a 的值: %d\n",a);
printf("請輸入一個字元: ");
scanf("%c",&b);
printf("b 的值: %c",b);
}
--------------------------------結果---------------------------------
請輸入一個整數: 1
a 的值: 1
請輸入一個字元: b 的值:
我們可以看到a變數是正常存取以及輸出的,但b是怎樣?照理來說我應該是要可以輸入一個字元的,但這邊是直接輸入完a就結束了。其實,根據我們剛剛所講,輸入的字會先存進緩衝區,所以當我輸入1並按下enter之後,緩衝區就會有了 "1\n" 這些字。你沒看錯,因為為了表示輸出結束我必須按下enter,所以也就會連\n(換行)也一起存了進去。
接著,第一個scanf會以\n為間隔,將前面的字串轉為%d的整數,所以1被賦值給了a,也就是說\n被獨留下來,導致緩衝區還有資料,也因此第二個scanf才會直接把\n給了b,而不是等待使用者輸入。
那這個問題其實很好解決,我們只要把程式碼改成這樣:
# include <stdio.h>
int main(void){
int a;
char b;
printf("請輸入一個整數: ");
scanf("%d",&a);
printf("a 的值: %d\n",a);
printf("請輸入一個字元: ");
scanf(" %c",&b);//差別在於%c前面多了一個空格
printf("b 的值: %c",b);
}
--------------------------------結果---------------------------------
請輸入一個整數: 5
a 的值: 5
請輸入一個字元: 6
b 的值: 6
--------------------------------或是---------------------------------
#include <stdio.h>
int main(void) {
int a;
char b;
printf("請輸入一個整數: ",a);
scanf("%d",&a);
printf("a的值為: %d\n",a);
b = getchar();//先把\n拿走,因此緩衝區會清空,scanf就會等使用者輸入了
printf("請輸入一個字元: ",a);
scanf("%c",&b);
printf("b的值為: %c\n",b);
}
--------------------------------結果---------------------------------
請輸入一個整數: 6
a的值為: 6
請輸入一個字元: 6
b的值為: 6
第一個的原理在於說,sanf中的空白符(空格、換格、\n的換行)會吃掉緩衝區所有的空白符,直到遇到非空白符才會停止。倘若到最後緩衝區都空了,也依舊沒有非空白符,則會等待使用者輸入。
以上面為例,淺談scanf及緩衝區的流程為:
1、使用者輸入6
2、電腦緩衝區取得6\n
3、scanf把6轉為%d,並賦值給a變數,緩衝區只剩\n
4、第二個scanf讀取到空格後,開始吃掉所有空白符
5、\n被吃掉,緩衝區是空的
6、因為是空的,所以等待使用者輸入字元
7、輸入6,電腦讀取6並轉為%c,賦值給變數b
第二個的方法則很簡單,getchar()會直接讀取緩衝區中的一個字元,倘若輸入為6 (數字6以及一個空格),則會產生這種結果:
(以上方第二個方法的getchar為例)
--------------------------------結果---------------------------------
請輸入整數: 6 (6+1個空格)
a的值為: 6
請輸入整數: b的值為:
因為getchar只會讀取一個字元,導致它只取走一個空格,緩衝區仍剩下\n,致使b直接存取了\n。淺談流程如下:
1、使用者輸入6 (6+1個空格)
2、電腦緩衝區取得6 \n
3、scanf把6轉為%d,並賦值給a變數,緩衝區只剩 \n
4、getchar()讀取了1個空格,緩衝區只剩\n
5、電腦讀取\n並轉為%c,賦值給變數b
gets
讀取所有緩衝區的字元,直到遇到換行符號,並化為字串(包含換行符號一起帶走,但輸出不會換行)給予指定變數。過程中倘若遇到空格或是製表符號、跳格符號,也會一起化為字串,不會因此停止讀取。
#include <stdio.h>
int main(void) {
char b[50];
printf("請輸入整數: ",a);
gets(b);
printf("b的值為: %s\n",b);
}
--------------------------------結果---------------------------------
請輸入字串: 我好餓
b的值為: 我好餓
gets與scanf差異
△分隔符號
對於scanf而言,一遇到空白字符就等於一個分隔。而gets則是指遇到換行才會結束讀取,即使是空格或是換格符號,也會做為字串繼續讀取。
△開頭遇換行符號
倘若緩衝區目前是"\n我好餓",一旦讀取之後,gets會直接判斷為\n而結束讀取。scanf則不一樣,倘若它存的是%s的字串,會忽略第一次\n,進而讀取後面的字串。所以在這個情況下,gets不能取得"我好餓",而scanf可以。程式碼如下:
#include <stdio.h>
int main(void) {
int a;
char b[100];
printf("請輸入一個整數: ");
scanf("%d", &a);//只取走1,遺留\n於緩衝區
printf("a的值為: %d\n",a);
printf("請輸入一串字: ");
gets(b);//一遇到\n直接結束存取
printf("b的值為: %s\n",b);
}
--------------------------------結果---------------------------------
請輸入一個整數: 1 (此時緩衝區為"1\n")
a的值為: 1
請輸入一串字: b的值為:
--------------------------------比較---------------------------------
#include <stdio.h>
int main(void) {
int a;
char b[100];
printf("請輸入一個整數: ");
scanf("%d", &a);//只取走1,遺留\n於緩衝區
printf("a的值為: %d\n",a);
printf("請輸入一串字: ");
scanf("%s", &b);//忽視掉第一個\n,則此時緩衝區為空,因此要求使用者輸入
printf("b的值為: %s\n",b);
}
--------------------------------結果---------------------------------
請輸入一個整數: 1
a的值為: 1
請輸入一串字: 我好餓
b的值為: 我好餓 (正常輸入與輸出)
△結尾的換行符號
當使用者輸入了一串字"我好餓",並按下enter後,此時進到緩衝區為"我好餓\n"。在經過讀取後,gets會連同\n一起讀取,不留下\n在緩衝區的。而scanf則只會讀取"我好餓",遺留\n在緩衝區當中。程式碼如下:
#include <stdio.h>
int main(void) {
char a[100];
char b;
printf("請輸入一串字: ");
scanf("%s", &a);
printf("a的值為: %s\n",a);
printf("請輸入一個字元: ");
scanf("%c", &b);
printf("b的值為: %c\n",b);
}
--------------------------------結果---------------------------------
請輸入一串字: 我好餓
a的值為: 我好餓
請輸入一個字元:
(b無法輸入是因為,緩衝區中還有一個\n,所以直接讓b存取了)
--------------------------------比較---------------------------------
#include <stdio.h>
int main(void) {
char a[100];
char b;
printf("請輸入一串字: ");
gets(a);//這邊改成gets()之後,\n就不會遺留緩衝區,因此緩衝區為空
printf("a的值為: %s\n",a);
printf("請輸入一個字元: ");
scanf("%c", &b);//因為緩衝區為空,導致scanf要求使用者輸入
printf("b的值為: %c\n",b);
}
--------------------------------結果---------------------------------
請輸入一串字: 我好累:(
a的值為: 我好累:(
請輸入一個字元: 1
b的值為: 1