主頁 > 後端開發 > C 語言中的基本資料型別

C 語言中的基本資料型別

2021-06-22 06:18:45 後端開發

目錄
  • 一、int 型別
    • 1.1 宣告 int 變數
    • 1.2 初始化變數
    • 1.3 int 型別常量
    • 1.4 列印 int 值
    • 1.5 八進制和十六進制
    • 1.6 顯示八進制和十六進制
  • 二、其他整數型別
    • 2.1 宣告其他整數型別
    • 2.2 使用多種整數型別的原因
    • 2.3 long 常量和 long long 常量
    • 2.4 列印 short、long、long long 和 unsigned 型別
  • 三、使用字符:char 型別
    • 3.1 宣告 char 型別變數
    • 3.2 字符常量和初始化
    • 3.3 非列印字符
    • 3.4 列印字符
    • 3.5 有符號還是無符號
  • 四、_Bool 型別
  • 五、可移植型別:stdint.h 和 inttypes.h
  • 六、float、double 和 long double
    • 6.1 宣告浮點型變數
    • 6.2 浮點型常量
    • 6.3 列印浮點值
    • 6.4 浮點值的上溢和下溢
  • 七、復數和虛數型別
  • 八、其他型別
  • 九、型別大小
  • 十、小結:基本資料型別
  • 十一、小結:如何宣告簡單變數

本文將詳細介紹 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 語言中的基本資料型別

(完)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288253.html

標籤:其他

上一篇:Python eval 與 exec 函式區別 - Python零基礎入門教程

下一篇:MySQL 到 ES 資料實時同步技術架構

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more