小屋創作

日誌2020-10-08 09:18

[達人專欄] 跟著豬腳 C 起來:程式如果不能輸入資料,那有什麼用?

作者:解凍豬腳

 
  前篇:跟著豬腳 C 起來:條件分歧,邏輯整個都出來了

  其實這篇正常來說早在當初最前面講 printf 的時候就該一起寫,但我個人覺得如果不先把條件分歧之類的基礎工具講完,那麼就算能讓使用者輸入資料進來,好像也沒有什麼意義,所以擺到今天才講。

  大部分的 3C 產品,正常來說總會有個螢幕(顯示面板)或是按鈕,讓你能夠對機器下達指令,而你也能同時透過顯示面板知道目前機器的狀態——這兩種基本元素,正是現代電腦具備的 I/O(input / output)功能。

  我們平時在電腦上面操作的程式也同理。撇除完全自動化的程式不談,很多時候我們總是需要按照使用者的需求來決定程式應該如何執行,這個程式才能夠真的算是有用。

  回憶一下上次示範的簡易身分證字號檢查器。你想想,如果程式設計師把想檢查的那組身分證字號寫死在 code 裡面,那這個程式不就只能檢查這麼一組身分證字號嗎?只能檢查一組身分證字號的檢查器,跟垃圾有什麼兩樣呢?

  所以,我們今天要來講的是 printf 的好兄弟——scanf!

  它的用法跟 printf 長得很像,先擺個字串把要讓使用者輸入的格式排出來,然後再把負責接收輸入資料的變數們依序放在後面:

    int x, y;
    printf("請輸入 x, y 的值並以空格隔開:");
    scanf("%d %d", &x, &y);

  這種時候使用者只要輸入「100 200」然後按下 enter,x 的值就會被設為 100、y 的值就會被設為 200。

  這裡值得注意的是,scanf 和 printf 有個地方不同,那就是負責接收資料的變數前面必須加上一個「&」符號,不過這裡涉及記憶體位址在函數的傳遞問題,這個就留到以後再一起講吧,總之現階段只要記得使用 scanf 的時候變數前面必須加上「&」就好了。

  也可以接收浮點數:

    float m;
    printf("請輸入 m 的值:");
    scanf("%f", &m);

  你也可以把 scanf 用來接收字串,然後以「%10s」來限定只能輸入 10 個字元(例如用在上次示範的身分證字號檢查器上面):

    char str[16];
    printf("請輸入要檢查的身分證字號:");
    scanf("%10s", &str);

  關於字串這裡要補充一點。我們知道字串實際上就是很多個 char 組成的 char 陣列,而且裡面每個位元存的都是一個 0~255 的數字,只是經由 ASCII 碼表轉換而已(這點我在《跟著豬腳 C 起來:各式各樣的資料型態》有講過),比如我們在一個長度為 16 的 char 陣列存入「A123456789」,這個陣列的內容就會是:

  {65, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 0, 0, 0, 0, 0}

  (以上用十進制表示)

  一般來說,0 在 ASCII 碼表上的意義是空字元,用在 C 語言的字串的時候可以表示一個字串的結束點,也就是說系統只要讀取到 0 就會認為這個字串已經讀取完畢,後半段的東西就會完全當作沒看見,你可以實驗看看:

    char str[16] = "ABC123456789";
    str[4] = 0;
    printf("%s", str);

  我們把 str[4] 這個 byte 的值強制歸零,使得這個陣列的內容變成:

  {65, 66, 67, 49, 0, 51, 52, 53, 54, 55, 56, 57, 0, 0, 0, 0}

  我們再直接把 str 的內容 print 出來,會得到「ABC1」的結果——系統發現 str[4] 的值是 0,認為這個字串已經結束了,因此只會把 str[0]、str[1]、str[2]、str[3] 當作是這個字串的有效部分。

  這個 0 很重要!在 C 語言裡,任何的字串都需要用到 0 來當作結束標誌。如果你宣告了一個長度為 16 bytes 的字串,那就代表它只能存 15 個字元,因為你必須留一個空間給 0 作為結束標誌來使用。

  如果我們把最後一個 byte 強制填入有效的字元,就會導致系統繼續讀取下去,有一定的概率接著輸出相鄰記憶體的內容:



  這當然是一件很危險的事情。寫程式的時候,最害怕的事情就是 overflow(溢位),如果是自己寫來跑點簡單任務的東西或許無傷大雅,但如果想寫的程式有重要用途或有一定安全性的需求,我們就應該在開發階段就先避免任何可能會導致溢位的設計,因為有能力的駭客會利用這種記憶體的溢位來挖掘漏洞,甚至藉此取得系統控制權限,而且這種事情在程式設計的領域已不鮮見。

  如果是想寫個「堪用的程式」,這篇已經可以算是最後一片拼圖了(當然這不是本系列的最後一篇)。不管寫得醜不醜,至少你已經把初級工具包蒐集完畢:輸出、輸入、變數、資料型態、條件分歧、陣列、迴圈。這些就算拿到不同的程式語言上面一樣通用,只是寫法或用法細節稍有不同而已。

  ——C 語言系列搞了這麼多篇,學寫程式怎麼可能不做題目呢?有興趣的話可以利用這系列到目前為止教過的內容,試著做做看:

  【第一題】
  1. 令使用者輸入一串文字(用 32 bytes 的 char 陣列儲存)
  2. 讓程式自動計算該串文字裡面有幾個大寫英文字母、幾個小寫英文字母、幾個數字

  【第二題】
  1. 令使用者輸入 5 組整數(直接用 int 儲存)
  2. 讓程式自動找出 5 組數字裡面最大的數和最小的數

  【第三題】
  1. 令使用者輸入兩組正整數,分別存入 x 和 y
  2. 讓程式自動計算 x+y 的值並顯示出來,不能有精確度的遺失
  3. x 和 y 各可以達到最多 50 位數,位數可以不同(比如 x 有 37 位、y 有 42 位)
  4. 全程不使用到 float、double、long

  做完不會有獎品,但你一定會有收穫。

  やらないか。

  
 

54

20

LINE 分享

相關創作

Fastapi 0.111.0 版本 提供新的CLI!

C語言列舉(enum)成員的走訪與避免被誤改

[量子位]沒有顯卡的年代,這群程式員用4行代碼優化遊戲

留言

開啟 APP

face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】