C 語言中的基本數據類型


本文將詳細介紹 C 語言的基本數據類型,包括如何聲明變量、如何表示字面值常量(如,5 或 2.78),以及典型的用法。一些老式的 C 語言編譯器無法支持這里提到的所有類型,請查閱你使用的編譯器文檔,了解可以使用哪些類型。

一、int 類型

C 語言提供了許多整數類型,為什么一種類型不夠用?因為 C 語言讓程序員針對不同情況選擇不同的類型。特別是,C 語言中的整數類型可表示不同的取值范圍和正負值。一般情況使用 int 類型即可,但是為滿足特定任務和機器的要求,還可以選擇其他類型。

int 類型是有符號整型,即 int 類型的值必須是整數,可以是正整數、負整數或零。其取值范圍依計算機系統而異。一般而言,存儲一個 int 要占用一個機器字長。因此,早期的 16 位 IBM PC 兼容機使用 16 位來存儲一個 int 值,其取值范圍(即 int 值的取值范圍)是 -32768~32767。目前的個人計算機一般是 32 位,因此用 32 位存儲一個 int 值。現在,個人計算機產業正逐步向着 64 位處理器發展,自然能存儲更大的整數。ISO C 規定 int 的取值范圍最小為 -32768~32767。一般而言,系統用一個特殊位的值表示有符號整數的正負號。

1.1 聲明 int 變量

先寫上 int,然后寫變量名,最后加上一個分號。要聲明多個變量,可以單獨聲明每個變量,也可在 int 后面列出多個變量名,變量名之間用逗號分隔。下面都是有效的聲明:

int erns;
int hogs, cows, goats;

可以分別在 4 條聲明中聲明各變量,也可以在一條聲明中聲明 4 個變量。兩種方法的效果相同,都為 4 個 int 大小的變量賦予名稱並分配內存空間。

以上聲明創建了變量,但是並沒有給它們提供值。變量如何獲得值?前面介紹過在程序中獲取值的兩種途徑。第 1 種途徑是賦值:

cows = 112;

第 2 種途徑是,通過函數(如,scanf())獲得值。接下來,我們着重介紹第 3 種途徑。

1.2 初始化變量

初始化(initialize)變量就是為變量賦一個初始值。在 C 語言中,初始化可以直接在聲明中完成。只需在變量名后面加上賦值運算符(=)和待賦給變量的值即可。如下所示:

int hogs = 21;
int cows = 32, goats = 14;
int dogs, cats = 94; /* 有效,但是這種格式很糟糕 */

以上示例的最后一行,只初始化了 cats,並未初始化 dogs。這種寫法很容易讓人誤認為 dogs 也被初始化為 94,所以最好不要把初始化的變量和未初始化的變量放在同一條聲明中。

簡而言之,聲明為變量創建和標記存儲空間,並為其指定初始值(如圖 4 所示)。

定義並初始化變量

圖 4 定義並初始化變量

1.3 int 類型常量

上面示例中出現的整數(21、32、14 和 94)都是整型常量或整型字面量。C 語言把不含小數點和指數的數作為整數。因此,22 和 -44 都是整型常量,但是 22.0 和 2.2E1 則不是。C 語言把大多數整型常量視為 int 類型,但是非常大的整數除外。詳見后面“long 常量和 long long 常量”小節對 long int 類型的討論。

1.4 打印 int 值

可以使用 printf() 函數打印 int 類型的值。%d 指明了在一行中打印整數的位置。%d 稱為轉換說明,它指定了 printf() 應使用什么格式來顯示一個值。格式化字符串中的每個 %d 都與待打印變量列表中相應的 int 值匹配。這個值可以是 int 類型的變量、int 類型的常量或其他任何值為 int 類型的表達式。作為程序員,要確保轉換說明的數量與待打印值的數量相同,編譯器不會捕獲這類型的錯誤。程序清單 2 演示了一個簡單的程序,程序中初始化了一個變量,並打印該變量的值、一個常量值和一個簡單表達式的值。另外,程序還演示了如果粗心犯錯會導致什么結果。

程序清單 2 print1.c 程序

/* print1.c - 演示printf()的一些特性 */
#include <stdio.h>
int main(void)
{
     int ten = 10;
     int two = 2;

     printf("Doing it right: ");
     printf("%d minus %d is %d\n", ten, 2, ten - two);
     printf("Doing it wrong: ");
     printf("%d minus %d is %d\n", ten);  // 遺漏2個參數

     return 0;
}

編譯並運行該程序,輸出如下:

Doing it right: 10 minus 2 is 8
Doing it wrong: 10 minus 16 is 1650287143

在第一行輸出中,第 1 個 %d 對應 int 類型變量 ten;第 2 個 %d 對應 int 類型常量 2;第 3 個 %d 對應 int 類型表達式 ten - two 的值。在第二行輸出中,第 1 個 %d 對應 ten 的值,但是由於沒有給后兩個 %d 提供任何值,所以打印出的值是內存中的任意值(讀者在運行該程序時顯示的這兩個數值會與輸出示例中的數值不同,因為內存中存儲的數據不同,而且編譯器管理內存的位置也不同)。

你可能會抱怨編譯器為何不能捕獲這種明顯的錯誤,但實際上問題出在 printf() 不尋常的設計。大部分函數都需要指定數目的參數,編譯器會檢查參數的數目是否正確。但是,printf() 函數的參數數目不定,可以有 1 個、2 個、3 個或更多,編譯器也愛莫能助。記住,使用 printf() 函數時,要確保轉換說明的數量與待打印值的數量相等。

1.5 八進制和十六進制

通常,C 語言都假定整型常量是十進制數。然而,許多程序員很喜歡使用八進制和十六進制數。因為 8 和 16 都是 2 的冪,而 10 卻不是。顯然,八進制和十六進制記數系統在表達與計算機相關的值時很方便。例如,十進制數 65536 經常出現在 16 位機中,用十六進制表示正好是 10000。另外,十六進制數的每一位的數恰好由 4 位二進制數表示。例如,十六進制數 3 的二進制數是 0011,十六進制數 5 的二進制數是 0101。因此,十六進制數35的位組合(bit pattern)是 00110101,十六進制數 53 的位組合是 01010011。這種對應關系使得十六進制和二進制的轉換非常方便。但是,計算機如何知道 10000 是十進制、十六進制還是二進制?在 C 語言中,用特定的前綴表示使用哪種進制。0x 或 0X 前綴表示十六進制值,所以十進制數 16 表示成十六進制是 0x10 或 0X10。與此類似,0 前綴表示八進制。例如,十進制數 16 表示成八進制是 020。

要清楚,使用不同的進制數是為了方便,不會影響數被存儲的方式。也就是說,無論把數字寫成 16、020 或 0x10,存儲該數的方式都相同,因為計算機內部都以二進制進行編碼。

1.6 顯示八進制和十六進制

在 C 程序中,既可以使用也可以顯示不同進制的數。不同的進制要使用不同的轉換說明。以十進制顯示數字,使用 %d;以八進制顯示數字,使用 %o;以十六進制顯示數字,使用 %x。另外,要顯示各進制數的前綴 0、0x 和 0X,必須分別使用 %#o%#x%#X。程序清單 3 演示了一個小程序(回憶一下,在某些集成開發環境(IDE)下編寫的代碼中插入 getchar(); 語句,程序在執行完畢后不會立即關閉執行窗口)。

程序清單 3 bases.c 程序

/* bases.c--以十進制、八進制、十六進制打印十進制數100 */
#include <stdio.h>
int main(void)
{
     int x = 100;

     printf("dec = %d; octal = %o; hex = %x\n", x, x, x);
     printf("dec = %d; octal = %#o; hex = %#x\n", x, x, x);

     return 0;
}

編譯並運行該程序,輸出如下:

dec = 100; octal = 144; hex = 64
dec = 100; octal = 0144; hex = 0x64

該程序以 3 種不同記數系統顯示同一個值。printf() 函數做了相應的轉換。注意,如果要在八進制和十六進制值前顯示 0 和 0x 前綴,要分別在轉換說明中加入 #。

二、其他整數類型

初學 C 語言時,int 類型應該能滿足大多數程序的整數類型需求。盡管如此,還應了解一下整型的其他形式。

C 語言提供 3 個附屬關鍵字修飾基本整數類型:short、long 和 unsigned。應記住以下幾點。

  • short int 類型(或者簡寫為 short)占用的存儲空間可能比 int 類型少,常用於較小數值的場合以節省空間。與 int 類似,short 是有符號類型。
  • long int 或 long 占用的存儲空間可能比 int 多,適用於較大數值的場合。與 int 類似,long 是有符號類型。
  • long long int 或 long long(C99 標准加入)占用的存儲空間可能比 long 多,適用於更大數值的場合。該類型至少占 64 位。與 int 類似,long long 是有符號類型。
  • unsigned int 或 unsigned 只用於非負值的場合。這種類型與有符號類型表示的范圍不同。例如,16 位 unsigned int 允許的取值范圍是 0~65535,而不是 -32768~32767。用於表示正負號的位現在用於表示另一個二進制位,所以無符號整型可以表示更大的數。
  • 在 C90 標准中,添加了 unsigned long int 或 unsigned long 和 unsigned short int 或 unsigned short 類型。C99 標准又添加了 unsigned long long int 或 unsigned long long。
  • 在任何有符號類型前面添加關鍵字 signed,可強調使用有符號類型的意圖。例如,short、short int、signed short、signed short int 都表示同一種類型。

2.1 聲明其他整數類型

其他整數類型的聲明方式與 int 類型相同,下面列出了一些例子。不是所有的 C 編譯器都能識別最后 3 條聲明,最后一個例子所有的類型是 C99 標准新增的。

long int estine;
long johns;
short int erns;
short ribs;
unsigned int s_count;
unsigned players;
unsigned long headcount;
unsigned short yesvotes;
long long ago;

2.2 使用多種整數類型的原因

為什么說 short 類型“可能”比 int 類型占用的空間少,long 類型“可能”比 int 類型占用的空間多?因為 C 語言只規定了 short 占用的存儲空間不能多於 int,long 占用的存儲空間不能少於 int。這樣規定是為了適應不同的機器。例如,過去的一台運行 Windows 3.x 的機器上,int 類型和 short 類型都占 16 位,long 類型占 32 位。后來,Windows 和蘋果系統都使用 16 位存儲 short 類型,32 位存儲 int 類型和 long 類型(使用 32 位可以表示的整數數值超過 20 億)。現在,計算機普遍使用 64 位處理器,為了存儲 64 位的整數,才引入了 long long 類型。

現在,個人計算機上最常見的設置是,long long 占 64 位,long 占 32 位,short 占 16 位,int 占 16 位或 32 位(依計算機的自然字長而定)。原則上,這 4 種類型代表 4 種不同的大小,但是在實際使用中,有些類型之間通常有重疊。

C 標准對基本數據類型只規定了允許的最小大小。對於 16 位機,short 和 int 的最小取值范圍是 [−32768,32767];對於 32 位機,long 的最小取值范圍是 [−2147483648,2147483647]。對於 unsigned short 和 unsigned int,最小取值范圍是 [0,65535];對於 unsigned long,最小取值范圍是 [0,4294967295]。long long 類型是為了支持 64 位的需求,最小取值范圍是 [−9223372036854775808,9223372036854775807];unsigned long long 的最小取值范圍是 [0,18446744073709551615]。如果要開支票,這個數是一千八百億億六千七百四十四萬億零七百三十七億零九百五十五萬一千六百一十五。但是,誰會去數?

int 類型那么多,應該如何選擇?首先,考慮 unsigned 類型。這種類型的數常用於計數,因為計數不用負數。而且,unsigned 類型可以表示更大的正數。

如果一個數超出了 int 類型的取值范圍,且在 long 類型的取值范圍內時,使用 long 類型。然而,對於那些 long 占用的空間比 int 大的系統,使用 long 類型會減慢運算速度。因此,如非必要,請不要使用 long 類型。另外要注意一點:如果在 long 類型和 int 類型占用空間相同的機器上編寫代碼,當確實需要 32 位的整數時,應使用 long 類型而不是 int 類型,以便把程序移植到 16 位機后仍然可以正常工作。類似地,如果確實需要 64 位的整數,應使用 long long 類型。

如果在 int 設置為 32 位的系統中要使用 16 位的值,應使用 short 類型以節省存儲空間。通常,只有當程序使用相對於系統可用內存較大的整型數組時,才需要重點考慮節省空間的問題。使用 short 類型的另一個原因是,計算機中某些組件使用的硬件寄存器 是16 位。

2.3 long 常量和 long long 常量

通常,程序代碼中使用的數字(如,2345)都被存儲為 int 類型。如果使用 1000000 這樣的大數字,超出了 int 類型能表示的范圍,編譯器會將其視為 long int 類型(假設這種類型可以表示該數字)。如果數字超出 long 可表示的最大值,編譯器則將其視為 unsigned long 類型。如果還不夠大,編譯器則將其視為 long long 或 unsigned long long 類型(前提是編譯器能識別這些類型)。

八進制和十六進制常量被視為 int 類型。如果值太大,編譯器會嘗試使用 unsigned int。如果還不夠大,編譯器會依次使用 long、unsigned long、long long 和 unsigned long long 類型。

有些情況下,需要編譯器以 long 類型存儲一個小數字。例如,編程時要顯式使用 IBM PC 上的內存地址時。另外,一些 C 標准函數也要求使用 long 類型的值。要把一個較小的常量作為 long 類型對待,可以在值的末尾加上 l(小寫的 L)或 L 后綴。使用 L 后綴更好,因為 l 看上去和數字 1 很像。因此,在 int 為 16 位、long 為 32 位的系統中,會把 7 作為 16 位存儲,把 7L 作為 32 位存儲。l 或 L 后綴也可用於八進制和十六進制整數,如 020L 和 0x10L。

類似地,在支持 long long 類型的系統中,也可以使用 ll 或 LL 后綴來表示 long long 類型的值,如 3LL。另外,u 或 U 后綴表示 unsigned long long,如 5ull、10LLU、6LLU 或 9Ull。

整數溢出

如果整數超出了相應類型的取值范圍會怎樣?下面分別將有符號類型和無符號類型的整數設置為比最大值略大,看看會發生什么(printf() 函數使用 %u 說明顯示 unsigned int 類型的值)。

/* toobig.c-- 超出系統允許的最大int值*/
#include <stdio.h>
int main(void)
{
    int i = 2147483647; 
     unsigned int j = 4294967295;

     printf("%d %d %d\n", i, i+1, i+2);
     printf("%u %u %u\n", j, j+1, j+2);

     return 0;
}

在我們的系統下輸出的結果是:

2147483647    -2147483648    -2147483647
4294967295    0    1

可以把無符號整數 j 看作是汽車的里程表。當達到它能表示的最大值時,會重新從起始點開始。整數 i 也是類似的情況。它們主要的區別是,在超過最大值時,unsigned int 類型的變量 j 從 0 開始;而 int 類型的變量 i 則從 −2147483648 開始。注意,當 i 超出(溢出)其相應類型所能表示的最大值時,系統並未通知用戶。因此,在編程時必須自己注意這類問題。

溢出行為是未定義的行為,C 標准並未定義有符號類型的溢出規則。以上描述的溢出行為比較有代表性,但是也可能會出現其他情況。

2.4 打印 short、long、long long 和 unsigned 類型

打印 unsigned int 類型的值,使用 %u 轉換說明;打印 long 類型的值,使用 %ld 轉換說明。如果系統中 int 和 long 的大小相同,使用 %d 就行。但是,這樣的程序被移植到其他系統(int 和 long 類型的大小不同)中會無法正常工作。在 x 和 o 前面可以使用 l 前綴,%lx 表示以十六進制格式打印 long 類型整數,%lo 表示以八進制格式打印 long 類型整數。注意,雖然 C 允許使用大寫或小寫的常量后綴,但是在轉換說明中只能用小寫。

C 語言有多種 printf() 格式。對於 short 類型,可以使用h前綴。%hd 表示以十進制顯示 short 類型的整數,%ho 表示以八進制顯示 short 類型的整數。h 和 l 前綴都可以和 u 一起使用,用於表示無符號類型。例如,%lu表示打印 unsigned long 類型的值。程序清單 4 演示了一些例子。對於支持 long long 類型的系統,%lld%llu 分別表示有符號和無符號類型。

程序清單 4 print2.c 程序

/* print2.c--更多printf()的特性 */
#include <stdio.h>
int main(void)
{
     unsigned int un = 3000000000; /* int為32位和short為16位的系統 */
     short end = 200;
     long big = 65537;
     long long verybig = 12345678908642;

     printf("un = %u and not %d\n", un, un);
     printf("end = %hd and %d\n", end, end);
     printf("big = %ld and not %hd\n", big, big);
     printf("verybig= %lld and not %ld\n", verybig, verybig);

     return 0;
}

在特定的系統中輸出如下(輸出的結果可能不同):

un = 3000000000 and not -1294967296
end = 200 and 200
big = 65537 and not 1
verybig= 12345678908642 and not 1942899938

該例表明,使用錯誤的轉換說明會得到意想不到的結果。第 1 行輸出,對於無符號變量 un,使用 %d 會生成負值!其原因是,無符號值 3000000000 和有符號值 −129496296 在系統內存中的內部表示完全相同。因此,如果告訴 printf() 該數是無符號數,它打印一個值;如果告訴它該數是有符號數,它將打印另一個值。在待打印的值大於有符號值的最大值時,會發生這種情況。對於較小的正數(如 96),有符號和無符號類型的存儲、顯示都相同。

第 2 行輸出,對於 short 類型的變量 end,在 printf() 中無論指定以 short 類型(%hd)還是 int 類型(%d)打印,打印出來的值都相同。這是因為在給函數傳遞參數時,C 編譯器把 short 類型的值自動轉換成 int 類型的值。你可能會提出疑問:為什么要進行轉換?h 修飾符有什么用?第 1 個問題的答案是,int 類型被認為是計算機處理整數類型時最高效的類型。因此,在 short 和 int 類型的大小不同的計算機中,用 int 類型的參數傳遞速度更快。第 2 個問題的答案是,使用 h 修飾符可以顯示較大整數被截斷成 short 類型值的情況。第 3 行輸出就演示了這種情況。把 65537 以二進制格式寫成一個 32 位數是 00000000000000010000000000000001。使用 %hd,printf() 只會查看后 16 位,所以顯示的值是 1。與此類似,輸出的最后一行先顯示了 verybig 的完整值,然后由於使用了 %ld,printf() 只顯示了存儲在后 32 位的值。

前面介紹過,程序員必須確保轉換說明的數量和待打印值的數量相同。以上內容也提醒讀者,程序員還必須根據待打印值的類型使用正確的轉換說明。

提示 匹配 printf() 說明符的類型

在使用printf()函數時,切記檢查每個待打印值都有對應的轉換說明,還要檢查轉換說明的類型是否與待打印值的類型相匹配。

三、使用字符:char 類型

char 類型用於存儲字符(如,字母或標點符號),但是從技術層面看,char 是整數類型。因為 char 類型實際上存儲的是整數而不是字符。計算機使用數字編碼來處理字符,即用特定的整數表示特定的字符。美國最常用的編碼是 ASCII 編碼。例如,在 ASCII 碼中,整數 65 代表大寫字母 A。因此,存儲字母 A 實際上存儲的是整數 65(許多 IBM 的大型主機使用另一種編碼——EBCDIC,其原理相同。另外,其他國家的計算機系統可能使用完全不同的編碼)。

標准 ASCII 碼的范圍是 0~127,只需 7 位二進制數即可表示。通常,char 類型被定義為 8 位的存儲單元,因此容納標准 ASCII 碼綽綽有余。許多其他系統(如 IMB PC 和蘋果 Macs)還提供擴展 ASCII 碼,也在 8 位的表示范圍之內。一般而言,C 語言會保證 char 類型足夠大,以存儲系統(實現 C 語言的系統)的基本字符集。

許多字符集都超過了 127,甚至多於 255。例如,日本漢字(kanji)字符集。商用的統一碼(Unicode)創建了一個能表示世界范圍內多種字符集的系統,目前包含的字符已超過 110000 個。國際標准化組織(ISO)和國際電工技術委員會(IEC)為字符集開發了 ISO/IEC 10646 標准。統一碼標准也與 ISO/IEC 10646 標准兼容。

C 語言把 1 字節定義為 char 類型占用的位(bit)數,因此無論是 16 位還是 32 位系統,都可以使用 char 類型。

3.1 聲明 char 類型變量

char 類型變量的聲明方式與其他類型變量的聲明方式相同。下面是一些例子:

char response;
char itable, latan;

以上聲明創建了 3 個 char 類型的變量:response、itable 和 latan。

3.2 字符常量和初始化

如果要把一個字符常量初始化為字母 A,不必背下 ASCII 碼,用計算機語言很容易做到。通過以下初始化把字母 A 賦給 grade 即可:

char grade = 'A';

在 C 語言中,用單引號括起來的單個字符被稱為字符常量(character constant)。編譯器一發現 'A',就會將其轉換成相應的代碼值。單引號必不可少。下面還有一些其他的例子:

char broiled;       /* 聲明一個char類型的變量 */
broiled = 'T';      /* 為其賦值,正確 */
broiled = T;        /* 錯誤!此時T是一個變量 */
broiled = "T";      /* 錯誤!此時"T"是一個字符串 */

如上所示,如果省略單引號,編譯器認為 T 是一個變量名;如果把 T 用雙引號括起來,編譯器則認為 "T" 是一個字符串。

實際上,字符是以數值形式存儲的,所以也可使用數字代碼值來賦值:

char grade = 65; /* 對於ASCII,這樣做沒問題,但這是一種不好的編程風格 */

在本例中,雖然 65 是 int 類型,但是它在 char 類型能表示的范圍內,所以將其賦值給 grade 沒問題。由於 65 是字母 A 對應的 ASCII 碼,因此本例是把 A 賦給 grade。注意,能這樣做的前提是系統使用 ASCII 碼。其實,用 'A' 代替 65 才是較為妥當的做法,這樣在任何系統中都不會出問題。因此,最好使用字符常量,而不是數字代碼值。

奇怪的是,C 語言將字符常量視為 int 類型而非 char 類型。例如,在 int 為 32 位、char 為 8 位的 ASCII 系統中,有下面的代碼:

char grade = 'B';

本來 'B' 對應的數值 66 存儲在 32 位的存儲單元中,現在卻可以存儲在 8 位的存儲單元中(grade)。利用字符常量的這種特性,可以定義一個字符常量 'FATE',即把 4 個獨立的 8 位 ASCII 碼存儲在一個 32 位存儲單元中。如果把這樣的字符常量賦給 char 類型變量 grade,只有最后 8 位有效。因此,grade 的值是 'E'

3.3 非打印字符

單引號只適用於字符、數字和標點符號,瀏覽 ASCII 表會發現,有些 ASCII 字符打印不出來。例如,一些代表行為的字符(如,退格、換行、終端響鈴或蜂鳴)。C 語言提供了 3 種方法表示這些字符。

第 1 種方法前面介紹過——使用 ASCII 碼。例如,蜂鳴字符的 ASCII 值是 7,因此可以這樣寫:

char beep = 7;

第 2 種方法是,使用特殊的符號序列表示一些特殊的字符。這些符號序列叫作轉義序列(escape sequence)。表 2 列出了轉義序列及其含義。

把轉義序列賦給字符變量時,必須用單引號把轉義序列括起來。例如,假設有下面一行代碼:

char nerf = '\n';

稍后打印變量 nerf 的效果是,在打印機或屏幕上另起一行。

表 2 轉義序列

轉義序列 含義
\a 警報(ANSI C)
\b 退格
\f 換頁
\n 換行
\r 回車
\t 水平制表符
\v 垂直制表符
\\ 反斜杠(\)
\' 單引號
\" 雙引號
\? 問號
\0oo 八進制值(oo必須是有效的八進制數,即每個o可表示07中的一個數)
\xhh 十六進制值(hh必須是有效的十六進制數,即每個h可表示0f中的一個數)

現在,我們來仔細分析一下轉義序列。使用 C90 新增的警報字符(\a)是否能產生聽到或看到的警報,取決於計算機的硬件,蜂鳴是最常見的警報(在一些系統中,警報字符不起作用)。C 標准規定警報字符不得改變活躍位置。標准中的活躍位置(active position)指的是顯示設備(屏幕、電傳打字機、打印機等)中下一個字符將出現的位置。簡而言之,平時常說的屏幕光標位置就是活躍位置。在程序中把警報字符輸出在屏幕上的效果是,發出一聲蜂鳴,但不會移動屏幕光標。

接下來的轉義字符 \b\f\n\r\t\v 是常用的輸出設備控制字符。了解它們最好的方式是查看它們對活躍位置的影響。換頁符(\f)把活躍位置移至下一頁的開始處;換行符(\n)把活躍位置移至下一行的開始處;回車符(\r)把活躍位置移動到當前行的開始處;水平制表符(\t)將活躍位置移至下一個水平制表點(通常是第 1 個、第 9 個、第 17 個、第 25 個等字符位置);垂直制表符(\v)把活躍位置移至下一個垂直制表點。

這些轉義序列字符不一定在所有的顯示設備上都起作用。例如,換頁符和垂直制表符在 PC 屏幕上會生成奇怪的符號,光標並不會移動。只有將其輸出到打印機上時才會產生前面描述的效果。

接下來的 3 個轉義序列(\\\'\")用於打印 \'" 字符(由於這些字符用於定義字符常量,是 printf() 函數的一部分,若直接使用它們會造成混亂)。如果打印下面一行內容:

Gramps sez, "a \ is a backslash."

應這樣編寫代碼:

printf("Gramps sez, \"a \\ is a backslash.\"\n");

表 2 中的最后兩個轉義序列(\0oo 和 \xhh)是 ASCII 碼的特殊表示。如果要用八進制 ASCII 碼表示一個字符,可以在編碼值前面加一個反斜杠(\)並用單引號括起來。例如,如果編譯器不識別警報字符(\a),可以使用 ASCII 碼來代替:

beep = '\007';

可以省略前面的 0,'\07' 甚至 '\7' 都可以。即使沒有前綴 0,編譯器在處理這種寫法時,仍會解釋為八進制。

從 C90 開始,不僅可以用十進制、八進制形式表示字符常量,C 語言還提供了第 3 種選擇——用十六進制形式表示字符常量,即反斜杠后面跟一個 x 或 X,再加上 1~3 位十六進制數字。例如,Ctrl+P 字符的 ASCII 十六進制碼是 10(相當於十進制的 16),可表示為 '\x10''\x010'。圖 5 列出了一些整數類型的不同進制形式。

int 系列類型的常量寫法示例

圖 5 int 系列類型的常量寫法示例

使用 ASCII 碼時,注意數字和數字字符的區別。例如,字符 4 對應的 ASCII 碼是 52。'4' 表示字符 4,而不是數值 4。

關於轉義序列,讀者可能有下面 3 個問題。

  • 上面最后一個例子(printf("Gramps sez, \"a \\ is a backslash.\"\n")),為何沒有用單引號把轉義序列括起來?無論是普通字符還是轉義序列,只要是雙引號括起來的字符集合,就無需用單引號括起來。雙引號中的字符集合叫作字符串。注意,該例中的其他字符(G、r、a、m、p、s 等)都沒有用單引號括起來。與此類似,printf("Hello!\007\n"); 將打印 Hello! 並發出一聲蜂鳴,而 printf("Hello!7\n"); 則打印 Hello!7。不是轉義序列中的數字將作為普通字符被打印出來。
  • 何時使用 ASCII 碼?何時使用轉義序列?如果要在轉義序列(假設使用 '\f')和 ASCII 碼('\014')之間選擇,請選擇前者(即 '\f')。這樣的寫法不僅更好記,而且可移植性更高。'\f' 在不使用 ASCII 碼的系統中,仍然有效。
  • 如果要使用 ASCII 碼,為何要寫成 '\032' 而不是 032?首先,'\032' 能更清晰地表達程序員使用字符編碼的意圖。其次,類似 \032 這樣的轉義序列可以嵌入 C 的字符串中,如 printf("Hello!\007\n"); 中就嵌入了 \007

3.4 打印字符

printf() 函數用 %c 指明待打印的字符。前面介紹過,一個字符變量實際上被存儲為 1 字節的整數值。因此,如果用 %d 轉換說明打印 char 類型變量的值,打印的是一個整數。而 %c 轉換說明告訴 printf() 打印該整數值對應的字符。程序清單 5 演示了打印 char 類型變量的兩種方式。

程序清單 5 charcode.c 程序

/* charcode.c-顯示字符的代碼編號 */
#include <stdio.h>
int main(void)
{
     char ch;

     printf("Please enter a character.\n");
     scanf("%c", &ch);   /* 用戶輸入字符 */
     printf("The code for %c is %d.\n", ch, ch);

     return 0;
}

運行該程序后,輸出示例如下:

Please enter a character.
C
The code for C is 67.

運行該程序時,在輸入字母后不要忘記按下 Enter 或 Return 鍵。隨后,scanf() 函數會讀取用戶輸入的字符,& 符號表示把輸入的字符賦給變量 ch。接着,printf() 函數打印 ch 的值兩次,第 1 次打印一個字符(對應代碼中的 %c),第 2 次打印一個十進制整數值(對應代碼中的 %d)。注意,printf() 函數中的轉換說明決定了數據的顯示方式,而不是數據的存儲方式(見圖 6)。

數據顯示和數據存儲

圖 6 數據顯示和數據存儲

3.5 有符號還是無符號

有些 C 編譯器把 char 實現為有符號類型,這意味着 char 可表示的范圍是 -128~127。而有些 C 編譯器把 char 實現為無符號類型,那么 char 可表示的范圍是 0~255。請查閱相應的編譯器手冊,確定正在使用的編譯器如何實現 char 類型。或者,可以查閱 limits.h 頭文件。

根據 C90 標准,C 語言允許在關鍵字 char 前面使用 signed 或 unsigned。這樣,無論編譯器默認 char 是什么類型,signed char 表示有符號類型,而 unsigned char 表示無符號類型。這在用 char 類型處理小整數時很有用。如果只用 char 處理字符,那么 char 前面無需使用任何修飾符。

四、_Bool 類型

C99 標准添加了 _Bool 類型,用於表示布爾值,即邏輯值 true 和 false。因為 C 語言用值 1 表示 true,值 0 表示 false,所以 _Bool 類型實際上也是一種整數類型。但原則上它僅占用 1 位存儲空間,因為對 0 和 1 而言,1 位的存儲空間足夠了。

程序通過布爾值可選擇執行哪部分代碼。

五、可移植類型:stdint.h 和 inttypes.h

C 語言提供了許多有用的整數類型。但是,某些類型名在不同系統中的功能不一樣。C99 新增了兩個頭文件 stdint.h 和 inttypes.h,以確保 C 語言的類型在各系統中的功能相同。

C 語言為現有類型創建了更多類型名。這些新的類型名定義在 stdint.h 頭文件中。例如,int32_t 表示 32 位的有符號整數類型。在使用 32 位 int 的系統中,頭文件會把 int32_t 作為 int 的別名。不同的系統也可以定義相同的類型名。例如,int 為 16 位、long 為 32 位的系統會把 int32_t 作為 long 的別名。然后,使用 int32_t 類型編寫程序,並包含 stdint.h 頭文件時,編譯器會把 int 或 long 替換成與當前系統匹配的類型。

上面討論的類型別名是精確寬度整數類型(exact-width integer type)的示例。int32_t 表示整數類型的寬度正好是 32 位。但是,計算機的底層系統可能不支持。因此,精確寬度整數類型是可選項。

如果系統不支持精確寬度整數類型怎么辦?C99 和 C11 提供了第 2 類別名集合。一些類型名保證所表示的類型一定是至少有指定寬度的最小整數類型。這組類型集合被稱為最小寬度類型(minimum width type)。例如,int_least8_t 是可容納 8 位有符號整數值的類型中寬度最小的類型的一個別名。如果某系統的最小整數類型是 16 位,可能不會定義 int8_t 類型。盡管如此,該系統仍可使用 int_least8_t 類型,但可能把該類型實現為 16 位的整數類型。

當然,一些程序員更關心速度而非空間。為此,C99 和 C11 定義了一組可使計算達到最快的類型集合。這組類型集合被稱為最快最小寬度類型(fastst minimum width type)。例如,int_fast8_t 被定義為系統中對 8 位有符號值而言運算最快的整數類型的別名。

另外,有些程序員需要系統的最大整數類型。為此,C99 定義了最大的有符號整數類型 intmax_t,可存儲任何有效的有符號整數值。類似地,uintmax_t 表示最大的無符號整數類型。順帶一提,這些類型有可能比 long long 和 unsigned long 類型更大,因為 C 編譯器除了實現標准規定的類型以外,還可利用 C 語言實現其他類型。例如,一些編譯器在標准引入 long long 類型之前,已提前實現了該類型。

C99 和 C11 不僅提供可移植的類型名,還提供相應的輸入和輸出。例如,printf() 打印特定類型時要求與相應的轉換說明匹配。如果要打印 int32_t 類型的值,有些定義使用 %d,而有些定義使用 %ld,怎么辦?C 標准針對這一情況,提供了一些字符串宏來顯示可移植類型。例如,inttypes.h 頭文件中定義了 PRId32 字符串宏,代表打印 32 位有符號值的合適轉換說明(如 d 或 l)。程序清單 6 演示了一種可移植類型和相應轉換說明的用法。

程序清單 6 altnames.c 程序

/* altnames.c -- 可移植整數類型名 */
#include <stdio.h>
#include <inttypes.h> // 支持可移植類型
int main(void)
{
     int32_t me32;     // me32是一個32位有符號整型變量

     me32 = 45933945;
     printf("First, assume int32_t is int: ");
     printf("me32 = %d\n", me32);
     printf("Next, let's not make any assumptions.\n");
     printf("Instead, use a \"macro\" from inttypes.h: ");
     printf("me32 = %" PRId32 "\n", me32);

     return 0;
}

該程序最后一個 printf() 中,參數 PRId32 被定義在 inttypes.h 中的 "d" 替換,因而這條語句等價於:

printf("me32 = %" "d" "\n", me32);

在 C 語言中,可以把多個連續的字符串組合成一個字符串,所以這條語句又等價於:

printf("me32 = %d\n", me32);

下面是該程序的輸出,注意,程序中使用了 \" 轉義序列來顯示雙引號:

First, assume int32_t is int: me32 = 45933945
Next, let's not make any assumptions.
Instead, use a "macro" from inttypes.h: me32 = 45933945

篇幅有限,無法介紹擴展的所有整數類型。本節主要是為了讓讀者知道,在需要時可進行這種級別的類型控制。

注意 對 C99/C11 的支持

C 語言發展至今,雖然 ISO 已發布了 C11 標准,但是編譯器供應商對 C99 的實現程度卻各不相同。一些編譯器仍未實現 inttypes.h 頭文件及其相關功能。

六、float、double 和 long double

各種整數類型對大多數軟件開發項目而言夠用了。然而,面向金融和數學的程序經常使用浮點數。C 語言中的浮點類型有 float、double 和 long double 類型。它們與 FORTRAN 和 Pascal 中的 real 類型一致。前面提到過,浮點類型能表示包括小數在內更大范圍的數。浮點數的表示類似於科學記數法(即用小數乘以 10 的冪來表示數字)。該記數系統常用於表示非常大或非常小的數。表 3 列出了一些示例。

表 3 記數法示例

數字 科學記數法 指數記數法
1000000000 1.0×109 1.0e9
123000 1.23×105 1.23e5
322.56 3.2256×102 3.2256e2
0.000056 5.6×10-5 5.6e-5

第 1 列是一般記數法;第 2 列是科學記數法;第 3 列是指數記數法(或稱為 e 記數法),這是科學記數法在計算機中的寫法,e 后面的數字代表 10 的指數。圖 7 演示了更多的浮點數寫法。

更多浮點數寫法示例

圖 7 更多浮點數寫法示例

C 標准規定,float 類型必須至少能表示 6 位有效數字,且取值范圍至少是 10-37~10+37。前一項規定指 float 類型必須能夠表示 33.333333 的前 6 位數字,而不是精確到小數點后 6 位數字。后一項規定用於方便地表示諸如太陽質量(2.0e30 千克)、一個質子的電荷量(1.6e-19 庫侖)或國家債務之類的數字。通常,系統存儲一個浮點數要占用 32 位。其中 8 位用於表示指數的值和符號,剩下 24 位用於表示非指數部分(也叫作尾數或有效數)及其符號。

C 語言提供的另一種浮點類型是 double(意為雙精度)。double 類型和 float 類型的最小取值范圍相同,但至少必須能表示 10 位有效數字。一般情況下,double 占用 64 位而不是 32 位。一些系統將多出的 32 位全部用來表示非指數部分,這不僅增加了有效數字的位數(即提高了精度),而且還減少了舍入誤差。另一些系統把其中的一些位分配給指數部分,以容納更大的指數,從而增加了可表示數的范圍。無論哪種方法,double 類型的值至少有 13 位有效數字,超過了標准的最低位數規定。

C 語言的第3種浮點類型是 long double,以滿足比 double 類型更高的精度要求。不過,C 只保證 long double 類型至少與 double 類型的精度相同。

6.1 聲明浮點型變量

浮點型變量的聲明和初始化方式與整型變量相同,下面是一些例子:

float noah, jonah;
double trouble;
float planck = 6.63e-34;
long double gnp;

6.2 浮點型常量

在代碼中,可以用多種形式書寫浮點型常量。浮點型常量的基本形式是:有符號的數字(包括小數點),后面緊跟 e 或 E,最后是一個有符號數表示 10 的指數。下面是兩個有效的浮點型常量:

-1.56E+12
2.87e-3

正號可以省略。可以沒有小數點(如,2E5)或指數部分(如,19.28),但是不能同時省略兩者。可以省略小數部分(如,3.E16)或整數部分(如,.45E-6),但是不能同時省略兩者。下面是更多的有效浮點型常量示例:

3.14159
.2
4e16
.8E-5
100.

不要在浮點型常量中間加空格:1.56 E+12(錯誤!)

默認情況下,編譯器假定浮點型常量是 double 類型的精度。例如,假設 some 是 float 類型的變量,編寫下面的語句:

some = 4.0 * 2.0;

通常,4.0 和 2.0 被存儲為 64 位的 double 類型,使用雙精度進行乘法運算,然后將乘積截斷成 float 類型的寬度。這樣做雖然計算精度更高,但是會減慢程序的運行速度。

在浮點數后面加上 f 或 F 后綴可覆蓋默認設置,編譯器會將浮點型常量看作 float 類型,如 2.3f 和 9.11E9F。使用 l 或 L 后綴使得數字成為 long double 類型,如 54.3l和4.32L。注意,建議使用 L 后綴,因為字母 l 和數字 1 很容易混淆。沒有后綴的浮點型常量是 double 類型。

C99 標准添加了一種新的浮點型常量格式——用十六進制表示浮點型常量,即在十六進制數前加上十六進制前綴(0x 或 0X),用 p 和 P 分別代替 e 和 E,用 2 的冪代替 10 的冪(即,p 計數法)。如下所示:

0xa.1fp10

十六進制 a 等於十進制 10,.1f 是 1/16 加上 15/256(十六進制 f 等於十進制 15),p10 是 210 或 1024。0xa.1fp10 表示的值是 (10 + 1/16 + 15/256)×1024(即,十進制 10364.0)。

注意,並非所有的編譯器都支持 C99 的這一特性。

6.3 打印浮點值

printf() 函數使用 %f 轉換說明打印十進制記數法的 float 和 double 類型浮點數,用 %e 打印指數記數法的浮點數。如果系統支持十六進制格式的浮點數,可用 a 和 A 分別代替 e 和 E。打印 long double 類型要使用 %Lf%Le%La 轉換說明。給那些未在函數原型中顯式說明參數類型的函數(如,printf())傳遞參數時,C 編譯器會把 float 類型的值自動轉換成 double 類型。程序清單 7 演示了這些特性。

程序清單 7 showf_pt.c 程序

/* showf_pt.c -- 以兩種方式顯示float類型的值 */
#include <stdio.h>
int main(void)
{
     float aboat = 32000.0;
     double abet = 2.14e9;
     long double dip = 5.32e-5;

     printf("%f can be written %e\n", aboat, aboat);
     // 下一行要求編譯器支持C99或其中的相關特性
     printf("And it's %a in hexadecimal, powers of 2 notation\n", aboat);
     printf("%f can be written %e\n", abet, abet);
     printf("%Lf can be written %Le\n", dip, dip);

     return 0;
}

該程序的輸出如下,前提是編譯器支持 C99/C11:

32000.000000 can be written 3.200000e+04
And it's 0x1.f4p+14 in hexadecimal, powers of 2 notation
2140000000.000000 can be written 2.140000e+09
0.000053 can be written 5.320000e-05

該程序示例演示了默認的輸出效果。

6.4 浮點值的上溢和下溢

假設系統的最大 float 類型值是 3.4E38,編寫如下代碼:

float toobig = 3.4E38 * 100.0f;
printf("%e\n", toobig);

會發生什么?這是一個上溢(overflow)的示例。當計算導致數字過大,超過當前類型能表達的范圍時,就會發生上溢。這種行為在過去是未定義的,不過現在 C 語言規定,在這種情況下會給 toobig 賦一個表示無窮大的特定值,而且 printf() 顯示該值為 inf 或 infinity(或者具有無窮含義的其他內容)。

當對一個很小的數做除法時,情況更為復雜。回憶一下,float 類型的數以指數和尾數部分來存儲。存在這樣一個數,它的指數部分是最小值,即由全部可用位表示的最小尾數值。該數字是 float 類型能用全部精度表示的最小數字。現在把它除以 2。通常,這個操作會減小指數部分,但是假設的情況中,指數已經是最小值了。所以計算機只好把尾數部分的位向右移,空出第 1 個二進制位,並丟棄最后一個二進制數。以十進制為例,把一個有 4 位有效數字的數(如,0.1234E-10)除以 10,得到的結果是 0.0123E-10。雖然得到了結果,但是在計算過程中卻損失了原末尾有效位上的數字。這種情況叫作下溢(underflow)。C 語言把損失了類型全精度的浮點值稱為低於正常的(subnormal)浮點值。因此,把最小的正浮點數除以 2 將得到一個低於正常的值。如果除以一個非常大的值,會導致所有的位都為 0。現在,C 庫已提供了用於檢查計算是否會產生低於正常值的函數。

還有另一個特殊的浮點值 NaN(not a number 的縮寫)。例如,給 asin() 函數傳遞一個值,該函數將返回一個角度,該角度的正弦就是傳入函數的值。但是正弦值不能大於 1,因此,如果傳入的參數大於 1,該函數的行為是未定義的。在這種情況下,該函數將返回 NaN 值,printf() 函數可將其顯示為 nan、NaN 或其他類似的內容。

浮點數舍入錯誤

給定一個數,加上 1,再減去原來給定的數,結果是多少?你一定認為是 1。但是,下面的浮點運算給出了不同的答案:

/* floaterr.c--演示舍入錯誤 */
#include <stdio.h>
int main(void)
{
    float a,b;

    b = 2.0e20 + 1.0;
    a = b - 2.0e20;
    printf("%f \n", a);

    return 0;
}

該程序的輸出如下:

0.000000        ←Linux系統下的老式gcc
-13584010575872.000000  ←Turbo C 1.5
4008175468544.000000    ←XCode 4.5、Visual Studio 2012、當前版本的gcc

得出這些奇怪答案的原因是,計算機缺少足夠的小數位來完成正確的運算。2.0e20 是 2 后面有 20 個 0。如果把該數加 1,那么發生變化的是第 21 位。要正確運算,程序至少要存儲 21 位數字。而 float 類型的數字通常只能存儲按指數比例縮小或放大的 6 或 7 位有效數字。在這種情況下,計算結果一定是錯誤的。另一方面,如果把 2.0e20 改成 2.0e4,計算結果就沒問題。因為 2.0e4 加 1 只需改變第 5 位上的數字,float 類型的精度足夠進行這樣的計算。

浮點數表示法

上一個方框中列出了由於計算機使用的系統不同,一個程序有不同的輸出。原因是,根據前面介紹的知識,實現浮點數表示法的方法有多種。為了盡可能地統一實現,電子和電氣工程師協會(IEEE)為浮點數計算和表示法開發了一套標准。現在,許多硬件浮點單元都采用該標准。2011 年,該標准被 ISO/IEC/IEEE 60559:2011 標准收錄。該標准作為 C99 和 C11 的可選項,符合硬件要求的平台可開啟。floaterr.c 程序的第 3 個輸出示例即是支持該浮點標准的系統顯示的結果。

七、復數和虛數類型

許多科學和工程計算都要用到復數和虛數。C99 標准支持復數類型和虛數類型,但是有所保留。一些獨立實現,如嵌入式處理器的實現,就不需要使用復數和虛數(VCR 芯片就不需要復數)。一般而言,虛數類型都是可選項。C11 標准把整個復數軟件包都作為可選項。

簡而言之,C 語言有 3 種復數類型:float _Complex、double _Complex 和 long double _Complex。例如,float _Complex 類型的變量應包含兩個 float 類型的值,分別表示復數的實部和虛部。類似地,C 語言的3種虛數類型是 float _Imaginary、double _Imaginary 和 long double _Imaginary。

如果包含 complex.h 頭文件,便可用 complex 代替 _Complex,用 imaginary 代替 _Imaginary,還可以用 I 代替 -1 的平方根。

為何 C 標准不直接用 complex 作為關鍵字來代替 _Complex,而要添加一個頭文件(該頭文件中把 complex 定義為 _Complex)?因為標准委員會考慮到,如果使用新的關鍵字,會導致以該關鍵字作為標識符的現有代碼全部失效。例如,之前的 C99,許多程序員已經使用 struct complex 定義一個結構來表示復數或者心理學程序中的心理狀況。讓 complex 成為關鍵字會導致之前的這些代碼出現語法錯誤。但是,使用 struct _Complex 的人很少,特別是標准使用首字母是下划線的標識符作為預留字以后。因此,標准委員會選定 _Complex 作為關鍵字,在不用考慮名稱沖突的情況下可選擇使用 complex。

八、其他類型

現在已經介紹完 C 語言的所有基本數據類型。有些人認為這些類型實在太多了,但有些人覺得還不夠用。注意,雖然 C 語言沒有字符串類型,但也能很好地處理字符串。

C 語言還有一些從基本類型衍生的其他類型,包括數組、指針、結構和聯合。盡管后面會詳細介紹這些類型,但是本文的程序示例中已經用到了指針〔指針(pointer)指向變量或其他數據對象位置〕。例如,在 scanf() 函數中用到的前綴 &,便創建了一個指針,告訴 scanf() 把數據放在何處。

九、類型大小

如何知道當前系統的指定類型的大小是多少?運行程序清單 8,會列出當前系統的各類型的大小。

程序清單 8 typesize.c 程序

/* typesize.c -- 打印類型大小 */
#include <stdio.h>
int main(void)
{
     /* C99為類型大小提供%zd轉換說明 */
     printf("Type int has a size of %zd bytes.\n", sizeof(int));
     printf("Type char has a size of %zd bytes.\n", sizeof(char));
     printf("Type long has a size of %zd bytes.\n", sizeof(long));
     printf("Type long long has a size of %zd bytes.\n",
               sizeof(long long));
     printf("Type double has a size of %zd bytes.\n",
              sizeof(double));
     printf("Type long double has a size of %zd bytes.\n",
              sizeof(long double));
     return 0;
}

sizeof 是 C 語言的內置運算符,以字節為單位給出指定類型的大小。C99 和 C11 提供 %zd 轉換說明匹配 sizeof 的返回類型。一些不支持 C99 和 C11 的編譯器可用 %u%lu 代替 %zd

該程序的輸出如下:

Type int has a size of 4 bytes.
Type char has a size of 1 bytes.
Type long has a size of 8 bytes.
Type long long has a size of 8 bytes.
Type double has a size of 8 bytes.
Type long double has a size of 16 bytes.

該程序列出了 6 種類型的大小,你也可以把程序中的類型更換成感興趣的其他類型。注意,因為 C 語言定義了 char 類型是 1 字節,所以 char 類型的大小一定是 1 字節。而在 char 類型為 16 位、double 類型為 64 位的系統中,sizeof 給出的 double 是4字節。在 limits.h 和 float.h 頭文件中有類型限制的相關信息。

順帶一提,注意該程序最后幾行 printf() 語句都被分為兩行,只要不在引號內部或一個單詞中間斷行,就可以這樣寫。

十、小結:基本數據類型

關鍵字:

基本數據類型由 11 個關鍵字組成:int、long、short、unsigned、char、float、double、signed、_Bool、_Complex 和 _Imaginary。

有符號整型:

有符號整型可用於表示正整數和負整數。

  • int ——系統給定的基本整數類型。C 語言規定 int 類型不小於 16 位。
  • short 或 short int ——最大的 short 類型整數小於或等於最大的 int 類型整數。C 語言規定 short 類型至少占 16 位。
  • long 或 long int ——該類型可表示的整數大於或等於最大的 int 類型整數。C 語言規定 long 類型至少占 32 位。
  • long long 或 long long int ——該類型可表示的整數大於或等於最大的 long 類型整數。long long 類型至少占 64 位。

一般而言,long 類型占用的內存比 short 類型大,int 類型的寬度要么和 long 類型相同,要么和 short 類型相同。例如,舊 DOS 系統的 PC 提供 16 位的 short 和 int,以及 32 位的 long;Windows 95 系統提供 16 位的 short 以及 32 位的 int 和 long。

無符號整型:

無符號整型只能用於表示零和正整數,因此無符號整型可表示的正整數比有符號整型的大。在整型類型前加上關鍵字 unsigned 表明該類型是無符號整型:unsigned int、unsigned long、unsigned short。單獨的 unsigned 相當於 unsigned int。

字符類型:

可打印出來的符號(如 A、& 和 +)都是字符。根據定義,char 類型表示一個字符要占用 1 字節內存。出於歷史原因,1 字節通常是 8 位,但是如果要表示基本字符集,也可以是 16 位或更大。

  • char ——字符類型的關鍵字。有些編譯器使用有符號的 char,而有些則使用無符號的 char。在需要時,可在 char 前面加上關鍵字 signed 或 unsigned 來指明具體使用哪一種類型。

布爾類型:

布爾值表示 true 和 false。C 語言用 1 表示 true,0 表示 false。

  • _Bool ——布爾類型的關鍵字。布爾類型是無符號 int 類型,所占用的空間只要能存儲 0 或 1 即可。

實浮點類型:

實浮點類型可表示正浮點數和負浮點數。

  • float ——系統的基本浮點類型,可精確表示至少 6 位有效數字。
  • double ——存儲浮點數的范圍(可能)更大,能表示比 float 類型更多的有效數字(至少 10 位,通常會更多)和更大的指數。
  • long double ——存儲浮點數的范圍(可能)比 double 更大,能表示比 double 更多的有效數字和更大的指數。

復數和虛數浮點數:

虛數類型是可選的類型。復數的實部和虛部類型都基於實浮點類型來構成:

  • float _Complex
  • double _Complex
  • long double _Complex
  • float _Imaginary
  • double _Imaginary
  • long double _Imaginary

十一、小結:如何聲明簡單變量

1.選擇需要的類型。

2.使用有效的字符給變量起一個變量名。

3.按以下格式進行聲明:

類型說明符 變量名;

類型說明符由一個或多個關鍵字組成。下面是一些示例:

int erest;
unsigned short cash;

4.可以同時聲明相同類型的多個變量,用逗號分隔各變量名,如下所示:

char ch, init, ans;

5.在聲明的同時還可以初始化變量:

float mass = 6.0E24;

原文:C 語言中的基本數據類型

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM