資料型態


程式在執行的過程中,需要運算許多的資訊,也需要儲存許多的資訊,資訊是儲存在記憶體空間中,由於資料的型態各不相同,在儲存時所需要的容量不一,不同的資料必須要配給不同的空間大小來儲存,因而有了資料型態(Data type)的規範。

C 的基本資料型態主要區分為整數(Integer)、浮點數(Float)、字元(Character),而這幾種還可以細分,如下所示:

  • 整數

    用來表示整數值,可以區分為 shortintlonglong long(C99),配置的記憶體長度在不同編譯器上各不相同,可容納的大小各不相同,例如,在 64 位元 Ubuntu 16.04 中的 gcc 編譯器下,intlong 為 8 位元組,在 Windows 10 使用 MinGW-w64,GNU 編譯器版本是 8.1.0 型態的話,intlong 為 4 位元組,長度越長,表示可表示的整數值範圍越大。

  • 浮點數

    用來表示小數值,可以區分為 floatdoublelong double,越後面的型態使用的記憶體空間越大,精度也就越高。

  • 字元

    charsizeof(char) 結果要是 1,基本上用來儲存字元資料,但沒有規定什麼是字元資料,也可用來儲存較小範圍的整數。

    與字元相關的型態,其實還有來自 C89 的 wchar_t,以及 C11 標準規範的 char16_tchar32_t(定義在 unchar.h 標頭檔)。

在 C11 標準中,建議包括 stdint.h 程式庫,使用 int8_tint16_tint32_tint64_t uint8_tuint16_tuint32_tuint64_t 等作為整數型態的宣告,以避免平台相依性的問題。

以上的資料型態在記憶體中佔有的大小依編譯器而有所差異,想知道這些資料型態在使用的平台上,佔有的記憶體空間有多少,可以使用 sizeof 運算子,它可以告訴你確實的記憶體大小,下面這個程式是個簡單的示範:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("型態\t\t大小(bytes)\n");
    printf("short\t\t%lu\n", sizeof(short));
    printf("int\t\t%lu\n", sizeof(int));
    printf("long\t\t%lu\n", sizeof(long));
    printf("float\t\t%lu\n", sizeof(float));
    printf("double\t\t%lu\n", sizeof(double));
    printf("long double\t%lu\n", sizeof(long double));
    printf("char\t\t%lu\n", sizeof(char));

    return 0;
}

其中 '\t' 是跳格字元,它相當於在主控台中按下 Tab 鍵的效果,可以用來對齊下一個顯示位置,%lu 為格式指定碼,表示該位置將放置一個 long unsigned 型態的整數,也就是由 sizeof() 所計算出來的數字取代,在 printf 與 scanf 中,將針對格式控制作進一步說明。

以下是執行結果:

型態            大小(bytes)
short           2
int             4
long            4
float           4
double          8
long double     12
char            1

由於平台不同,你的執行結果不一定會與這邊相同;因為每種資料型態佔有的記憶體大小不同,因而可以儲存的數值範圍也就不同。

例如在上例中,整數 int 的記憶體空間是 4 個位元組,可以儲存的整數範圍為 -2147483648 至 2147483647,如果儲存值超出這個範圍的話稱之為「溢值」 (Overflow),會造成程式不可預期的結果。

整數計算範圍的方式很簡單,例如 int 整數佔 4 位元組的話,可儲存範圍為 2^32 / 2,除以 2 是因為要分另一半儲存負整數的關係,整數的最左邊位元被用來表示正負號,如果最左邊位元為 0 表示正號,若為 1 表示負號。

在 C 中可以使用 signedunsigned 宣告有號、無號整數,無號整數最左邊位元不用來表示正負號,例如一個 8 位元字元被用來表示整數時,可以表示數值 -128 到 127,而一個無號字元可以表示 0 到 255 的正整數。

size_t 是個機器特定的無號型態,定義於 stddef.h 標頭檔,保證夠大可以容納任何物件,若資料結構具有長度或索引概念,而且需要很大的容量,例如陣列,可以使用 size_t 型態變數來實作長度或索引。

浮點數的儲存方式則是分為兩個部份,例如 float 佔四個位元組的話,前三個位元組用來儲存假數,後一個位元組儲存指數,例如 3.14 * 108, 3.14 儲存在前三位元組,而 8 儲存於後一位元組,所以 float 大致上可以儲存的範圍為 1038 到 10-38(就指數來算出的),而 double 可以大致儲存 10308 至 10^-308 之間的數值。

C 語言一開始並沒有布林值型態,而是將零值或 NULL 視為不成立,其他值都是成立,C99 有 bool 名稱,定義於 stdbool.h,用來表示布林數,是 C99 布林型態 _Bool 的巨集(也就是會編譯後會展開為 _Bool),在程式中佔一個位元組,也可以使用巨集 truefalse 字面常量(Literal constant) 來表示,展開後成為 1 與 0:

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    printf("sizeof(bool)\t%d\n", sizeof(bool));
    printf("sizeof(true)\t%d\n",  sizeof(true));
    printf("sizeof(false)\t%d\n",  sizeof(false));

    printf("true\t%d\n", true);
    printf("false\t%d\n", false);

    return 0;
}

執行結果:

sizeof(bool)    1
sizeof(true)    4
sizeof(false)   4
true    1
false   0