1. 程式人生 > >C語言筆記2 --新手常常犯的錯誤之scanf()

C語言筆記2 --新手常常犯的錯誤之scanf()

接著上次的輸出函式,這次我們來細細的講一下輸入函式:

scnaf();

同學們在第一節C語言課上,老師還沒要求我們從鍵盤輸入資料,只是單純的宣告變數,用一些基本運算子輸出結果。而在C語言中,有著輸出,必然有著輸入,就像英語中有 you,那麼肯定也有 I. 在用這個函式的時候同學們常常會出一些小毛病,而這些毛病,對於剛剛進入程式設計大門的同學來說,是很難發現的。 這裡我稍微詳細的介紹下這個與輸出對應的函式,輸入。 寫一個簡單的輸入函式的格式:

scanf(" 格式化字串 ", 地址列表);

有觀察到這裡與printf函式極其相似嘛,雙引號,逗號,分號。 格式化字串與printf函式一樣,也是由 % + x 構成:

%d %c %s %e %u

與printf不同的是,這裡逗號右邊是地址列表,同學們在寫程式碼的時候,有沒有好奇為啥要用到 & 這個符號? 這個符號在C語言中,在這個函式裡面叫做 “取地址符”。 同學們記得第一節課老師講的關於計算機中地址的概念嘛? 在我們宣告變數的時候,編譯器就會幫助我們為這個變數開闢一定大小的地址空間,用來存放資料,例如:

int num1, num2;          //開闢兩個大小為4個位元組的整形變數
double num3, num4;       //開闢了一個大小為8個位元組的雙精度變數

這裡的地址是一個標識,讓我們知道這個資料存放的位置。相當於你點外賣的時候,告訴商家你的地址是在XXXXX學校XX宿舍501號 這樣的。外賣相當於是資料,當外賣小哥把外賣送你宿舍的時候,也就相當於把資料存進了地址。 這裡的 & 作用就是告訴scanf函式這個我輸入的資料是要存進這個地址的,也可以說是寫進這個地址。 知道了這些之後,我們再來看下這個函式怎麼用: 比如我想求兩個整形變數相加減,那麼首先我肯定是要先有這兩個整形變數存在,不存在我資料放哪去?外賣小哥的外賣送去哪? 所以

int n1, n2;  //宣告兩個整形變數

接下來輸入,往這兩個變數裡面寫入資料,這時候,我們的scanf函式就派上用場啦:

scanf("%d %d", &a, &b);

這裡要解釋一點 : 在格式控制字串裡面,一般都不需要新增任何字元的,有些同學喜歡在裡面加上逗號:

scnaf("%d,%d", &a, &b);

這就導致你在輸入資料的時候,也要把逗號輸入進去。 可是這是為什麼呢? 因為,在我們把資料從鍵盤敲進顯示屏的時候,這些資料會以ASCII碼的形式存在 ”緩衝區", 例如我輸入12 74. 那麼此時存在緩衝區的資料就是3132 3734 (ASCII碼),然後scanf會從緩衝區掃描過去,從緩衝區讀走一個字元,相當於清空緩衝區,直到結束。 也許會有同學有疑問,這個中間有空格咋辦? 這個不用擔心,scanf會自動忽略這樣的空白字元,如大段空格,tab,回車。 也許還會問這個讀走的是ASCII碼呀,可是我要的是十進位制的啊? 這個也不用擔心,scanf會幫我們轉換成對應的的資料型別。 然後我們在回頭看一下,為啥要輸入逗號呢?因為在輸入資料的時候,若是沒有空格,scanf在掃描資料的時候,本來要寫入變數b地址的資料,寫進了逗號,故變數b也就沒寫入資料。 用執行結果來看: 在這裡插入圖片描述

變數b的值還是系統隨機分配的垃圾值(此值為上一個程式用過該地址後,未清空留下的值。比如有個程式利用了這塊地址,假設地址為0x93fb78, 裡面的資料為 -1234567. 用完之後裡面的值沒有清空,那麼在我這個程式碼中,變數b恰好分配到了這個地址,而我還未對該變數進行賦值操作,由系統分配,那麼該值就是上一次遺留的資料,也就是-1234567)。

若我b 賦初始值為 0 呢? 這就很直觀的反映了我輸入的資料74 並沒有寫入進b的地址,造成了資料丟失。 所以,在我們不是特別要求的情況下,最好還是不要在scanf函式裡面加東西,直接寫成:

scanf("%d %d",&n1, &n2);
scanf("%d%d",&n1, &n2);   //%d%d之間可以空格,也可以不用空格隔開

上面的對scanf忽略空白符的描述也可以作為理解輸入資料的時候,為什麼可以用回車隔開,也可以空格很多次,而不影響資料的寫入的理論基礎。

如果說printf是拼圖的時候一塊塊拼圖,那麼scanf函式就是拼圖的建造者(資料寫入) 它告訴printf函式這塊拼圖該往哪兒拼(資料輸出) 這裡我要對 取地址 & 講下我在學習C語言時候的理解: 取地址,我把地址理解為計算機中一個個小盒子,這個盒子叫做地址, 取地址的時候就是把這個盒子給拿出來,scanf函式從鍵盤獲取資料的時候, 把資料放進這個小盒子,在把盒子放到它原來的位置。 不同的資料型別對應著不同大小的盒子,double最大,int相對較小,而且double這個盒子能裝水(浮點,浮這個詞我想像成水<–浮力),int型別的卻不行,換句話說,double和int存放的資料型別是不一樣的。 這樣去看待的話是不是很好理解呢?

scnaf函式作為資料的寫入,有一個缺陷,在寫入字元陣列資料的時候,只會讀取第一個空格前的字元資料: 在這裡插入圖片描述 同學們看到空格是不是聯想到了前面的 scanf會忽略空白符 呢?它在讀取到第一個空格的時候就誤認為資料已經完畢了,所以把空格前的 Today 寫進了字元變數 str 在的地址。 後面的資料就存在了緩衝區。這也是scanf函式的一個缺陷。 ps: 在用scanf函式對字串進行操作的時候,不需要取地址符, 因為陣列名就是這個變數的首地址。 為了解決這個缺陷,我們需要用到gets()函式來輸入字串資料: 在這裡插入圖片描述

關於scanf函式的問題就先講到這裡了,最後再次總結下關於scanf函式涉及到的幾個方面: 0:取地址符 1:scanf會忽略空白符 2:輸入資料的時候,格式要跟 ”格式控制字串“ 一樣。有逗號輸入逗號,無逗號用空白符隔開資料即可。 3:資料在緩衝區,scanf讀取資料,轉換成我們需要的,清空緩衝區。

(如有錯誤歡迎指出)