小屋創作

日誌2018-12-02 20:00

[達人專欄] 跟著豬腳 C 起來:各式各樣的資料型態

作者:解凍豬腳


  前篇:跟著豬腳 C 起來:用電腦來操控資料吧

  講完了電腦儲存資料的基本概念以後,我們緊接著要來認識何謂「資料型態」。

  在大部分的程式語言當中,都對這些資料的「型態」做了規範。例如我們今天想要儲存一筆正整數,那我們就會預先告訴電腦說:「我在這裡儲存的資料是正整數,千萬不要把它誤認成別的東西了喔!」如此一來,就可以避免顯示出來的資料有問題。

  舉個例:假設今天桌上有三個沒有貼上任何標籤的罐子,裡面裝的都是無色液體,一瓶是礦泉水、一瓶是酒精、一瓶則是硫酸溶液。如果你需要補充水分,結果卻拿了硫酸溶液來喝,那後果想必是非常恐怖,因為你把不能喝的東西拿來喝了。

  這樣說起來也許有點抽象,那就實際來講講 C 語言的資料型態吧。

  在 C 語言當中,常用的資料型態及儲存範圍有以下幾種:

  字元 char(1 byte):0 ~ 255

  短整數 short(2 bytes):-32768 ~ 32767
  整數 int(4 bytes):-2147483648 ~ 2147483647
  長整數 long(4 bytes):-2147483648 ~ 2147483647

  浮點數 float(4 bytes):1.2e-38 ~ 3.4e38
  雙精度浮點數 double(8 bytes):2.2e-308 ~ 1.8e308

  ※ 1.2e-38 為科學記號,用以表示 1.2 × 10^-38,其它以此類推。

  這裡要注意:只要牽涉到 long、double 這兩個詞的,可能會因為編譯器不同而導致位元組數量不同。例如有一些 C 語言的編譯器就將 long 的大小定為 8 bytes(我手上的 Dev-C++ 附帶的編譯器則是 4 bytes)。其餘也有 long long 或是 long double 之類的型態,但一樣依編譯環境不同而相異,有興趣或有需要的可以再去找找看。

  我們上次提到了,在電腦裡面都是用 1 和 0 的組合來儲存資料,但是因為一個位元太小了,所以習慣用 8 個位元一組的方式來計量。

  以 short 來說,假設今天我想要儲存 4567 這個數,那這兩個連在一起的位元組就會分別被存入「00010001 11010111」。

  設計 C 語言的工程師為了方便(畢竟計算的時候常常需要用到負數),就把大部分的基本資料型態安插了一個稱為「補數」的機制(因為這個機制有點複雜,所以想要的人就自己去 Google 吧)。他們把大約其中一半的空間挪到負數,這樣就可以避免想用到負數的時候卻沒得使用的窘境。因此,當我們在使用 int、short、long、float、double 這幾種型態的時候,都是支援負數的。

  這也間接解釋了為何在楓之谷這款線上遊戲當中,玩家的持有金錢上限為 2,147,483,647 了,基本上我們可以推測,儲存玩家楓幣數量的資料型態大概就是使用一個 int。

  然而,有些時候我們希望表現的範圍大一點點(例如我們想要儲存一個 30 多億的數字),剛好又很確定不需要用到負數,那我們就可以使用 unsigned(無號、無負號)來修飾它:

    unsigned int x, y;
    x = 3147483647;
    y = x + 48763;
    printf("%u\n", x);
    printf("%u\n", y);

  執行結果如下:



  如此一來,我們就可以成功讓 x 和 y 的儲存範圍從 -2147483648 ~ 2147483647 變成 0 ~ 4294967295,即使儲存一組範圍在 2147483648 ~ 4294967295 之內的數字也沒有問題。另外需要注意,這裡得要使用「%u」來表示「無號十進位整數」,如果誤用了表示「有號十進位整數」的「%d」的話,那顯示出來的東西就又會變成負數了。

  上次強調過,身為一個優秀的程式設計師,必須先預想好所使用的變數可能會用到多大範圍,來決定接下來所使用的資料型態。

  double 型態固然方便,但如果我們總是把所有的變數都設為 double 的話,那就會造成佔用的記憶體空間過大,浪費電腦資源。

  所以,如果我們今天想要用 C 語言寫一個用來計算「從 1970 年 1 月 1 日至今已經經過了幾天」的程式,那我們就可以使用一個 unsigned short 來計算,這樣 0 ~ 65535 的範圍,就可以供它用到西元 2149 年都還能正常運作,卻只需佔用 2 bytes(當然如果你希望它能一直使用到西元 11761200 年而改用 unsigned int 的話,我也是沒意見啦……)。

  至於 float 跟 double,儲存的機制就更複雜了。它們除了其中第一個 bit 跟其它整數型態一樣都有正負號位之外,分成了「指數」和「小數」來儲存。詳細的原理我也不贅談(寫了的話這篇的篇幅會翻好幾倍),通常使用 float 就可以扛得住很多基本的運算,若需要更精確的計算結果再考慮改用 double。

  其中最特別的是 char。這個 char 通常用來表示「字元」,但因為電腦裡所有的資料都必須用數字來儲存,所以在 C 語言當中,儲存的文字是依照「ASCII」(美國資訊交換標準程式碼)系統來表示對應的關係。只要 Google 搜尋 ASCII,就可以查到 ASCII code 表。

    char x = 87;
    printf("%c", x);

  試著執行這兩句,就會發現 print 出來的東西是一個「W」字,跟 ASCII code 表吻合。基本上,幾乎所有的編碼在 0 ~ 126 範圍表示的內容,都是以 ASCII 為主,所以無論是繁體中文用的 Big5、簡體中文用的 GB 2312、日文用的 Shift_JIS、國際通用的 Unicode ……在這些編碼當中,想要顯示一般的英文字母和阿拉伯數字都是沒有問題的。

  因此,如果今天系統想要儲存「I am handsome.」就會需要使用連續的 14 個 byte 來儲存,這 14 個變數的內容依序如下:

  73, 32, 97, 109, 32, 104, 97, 110, 100, 115, 111, 109, 101, 46

  用十六進位表示的話就會是這樣:

  49, 20, 61, 6D, 20, 68, 61, 6E, 64, 73, 6F, 6D, 65, 2E

  如果你的手邊有 hex editor 相關軟體,那你還可以把「I am handsome.」存到記事本裡面,然後再用這類程式打開來看,就可以知道電腦是如何儲存這些文字。例圖如下:



  基本的資料型態,大概就是這樣。

  所以,如果你想要儲存這麼一句話,你可以這麼做:



  屁咧!當然不可能搞得這麼複雜。要是你真的這麼做,千萬別跟人說你的 C 語言是給豬腳教的,如果你想輸出兩百個字還不寫到死。

  為了解決這樣的麻煩,C 語言還提供了更好的方法可以一次宣告(甚至控制)一堆變數。

  下次我會說明 C 語言的陣列(array)和結構(structure)。只要會了這兩個,你的程度就會再往上提升一個檔次了。感謝你的閱覽,我們下次見啦。
 

69

24

LINE 分享

相關創作

[達人專欄] [ OpenGL 入門到入土 # 2] Compute shader、GPU Instance、DrawIndirect與實作Frustum culling

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

promise.all 有一個失敗就不會往下做其他promise了,如何無痛讓promise做事 - promise.all的陷阱

留言

開啟 APP

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

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