博客 / 詳情

返回

《C語言電子書-2026最新版》-C語言數據類型概述

大家好,我是良許,一個深耕嵌入式 12 年的老工程師,前世界 500 強高工。

我花了 3 個月時間,寫了一個 C 語言電子書,以非常通俗的語言跟大家講解 C 語言,把複雜的技術講得連小學生都能聽得懂,絕不是 AI 生成那種晦澀難懂的電子垃圾。

點擊此處免費領取 C 語言電子書

C 語言電子書目錄如下:

2.1 C語言數據類型概述

在我們的日常生活中,我們會遇到各種各樣的信息:數字、文字、圖片、聲音等等。比如你的年齡是一個數字,你的姓名是一段文字,你的照片是圖像信息。不同類型的信息需要用不同的方式來處理和存儲。

同樣地,在計算機程序中,我們也需要處理各種不同類型的數據。有時候我們需要存儲一個人的年齡,有時候需要存儲一個人的身高,有時候需要存儲一個人的姓名。這些不同種類的數據就需要用不同的數據類型來表示。

數據類型就像是給數據貼上的"標籤",告訴計算機這個數據是什麼類型的,應該如何處理。就像超市裏的商品都有標籤一樣,食品類商品有食品標籤,電子產品有電子產品標籤,不同的標籤決定了商品的處理方式。

2.1.1 數據類型分類

在C語言中,數據類型可以看作是一個大家族,這個家族有很多分支。讓我們用一個家族族譜的方式來理解C語言的數據類型分類。

整個C語言數據類型家族可以分為兩大主要分支:基本數據類型構造數據類型。這就像一個大家族分為"原生家庭成員"和"通過結合組成的新家庭"一樣。

1. 基本數據類型詳解

基本數據類型是C語言中最基礎、最原始的數據類型,就像化學中的原子一樣,它們是構成其他複雜數據類型的基礎。基本數據類型又可以細分為幾個小類:

整型數據類型

整型數據類型專門用來存儲整數,就像我們數學中學習的整數一樣:...,-3,-2,-1,0,1,2,3,...

在整型家族中,有好幾個成員,它們的區別主要在於能夠存儲的數值範圍不同:

  • int:這是最常用的整型,就像家族中的"長子",是整型家族的代表。它通常可以存儲-2147483648到2147483647之間的整數。為什麼是這個範圍呢?這與計算機的內部存儲方式有關,我們後面會詳細解釋。
  • short:這是"小弟弟",能存儲的數值範圍比int小,通常是-32768到32767。雖然範圍小,但佔用的內存空間也更少,在內存珍貴的嵌入式系統中很有用。
  • long:這是"大哥哥",能存儲的數值範圍比int大。在不同的系統中,long的大小可能不同,但它至少和int一樣大。
  • long long:這是"超級大哥",能存儲非常大的整數,範圍通常從-9223372036854775808到9223372036854775807。

每種整型還可以加上unsigned修飾符,表示"無符號",也就是隻能存儲非負數(0和正數)。這就像把負數的存儲空間也用來存儲正數,所以無符號類型能存儲的正數範圍會翻倍。

浮點型數據類型

浮點型用來存儲小數,比如3.14,2.718,0.5等等。為什麼叫"浮點"呢?這是因為小數點的位置是"浮動"的,可以在數字中的任何位置。

  • float:單精度浮點數,就像用普通的尺子測量長度,精度有限但夠用。它通常能提供大約6-7位有效數字的精度。
  • double:雙精度浮點數,就像用精密的遊標卡尺測量,精度更高。它通常能提供大約15-16位有效數字的精度。大多數情況下,我們使用double來處理小數。
  • long double:擴展精度浮點數,精度最高,但在不同系統中的具體實現可能不同。

字符型數據類型

char類型用來存儲單個字符,比如字母'A',數字'5',標點符號'!'等等。需要注意的是,字符要用單引號括起來,比如'A',而不是"A"。

有趣的是,在計算機內部,字符實際上是以數字的形式存儲的。每個字符都對應一個數字編碼,比如字母'A'對應數字65,字母'B'對應數字66。這套編碼標準叫做ASCII碼。這就像每個漢字都有一個拼音編碼一樣,計算機用數字來編碼字符。

2. 構造數據類型詳解

構造數據類型是由基本數據類型組合而成的更復雜的數據類型,就像用磚塊建造房子一樣,用基本數據類型構造更復雜的數據結構。

數組類型

數組就像是一排儲物櫃,每個櫃子裏可以放同樣類型的東西。比如,一個整型數組可以存儲一系列整數,就像一排櫃子裏都放着數字。

數組有一維數組、二維數組、多維數組等。一維數組像是一排櫃子,二維數組像是一個櫃子矩陣(行和列),三維數組像是一個立體的櫃子組合。

指針類型

指針是C語言中一個非常重要但也比較難理解的概念。指針就像是地址標籤,它不直接存儲數據,而是存儲數據的地址。

想象一下,你要告訴朋友你家在哪裏,你不會把整個房子搬過去給他看,而是告訴他你家的地址。指針就是這樣,它存儲的是數據在內存中的"地址"。

結構體類型

結構體允許我們把不同類型的數據組合在一起,就像填寫一張學生信息表一樣,可以包含姓名(字符串)、年齡(整數)、身高(浮點數)等不同類型的信息。

聯合體類型

聯合體比較特殊,它允許不同類型的數據共享同一塊內存空間。這就像一個多功能房間,有時候當卧室使用,有時候當客廳使用,但同一時間只能有一種用途。

枚舉類型

枚舉類型用來表示一組有限的選擇,比如一週的七天、一年的十二個月、交通燈的三種顏色等。這讓程序更容易理解和維護。

3. 自定義數據類型

除了C語言提供的基本數據類型,我們還可以使用typedef關鍵字來定義自己的數據類型。這就像給數據類型起別名一樣,讓程序更容易理解。

比如,我們可以定義:

typedef int StudentAge;  // 定義學生年齡類型
typedef float StudentHeight;  // 定義學生身高類型

這樣在程序中使用StudentAgeStudentHeight就更容易理解這些變量的用途。

2.1.2 數據在內存中的存儲

1. 內存的基本概念

要理解數據在內存中的存儲,我們首先要了解什麼是內存。計算機的內存就像一個巨大的儲物櫃,有無數個小格子,每個格子都有一個唯一的編號(地址),可以存儲一個字節的數據。

想象一下一個巨大的郵局,有無數個郵箱,每個郵箱都有一個唯一的編號。當你要寄信時,需要知道收信人的郵箱編號;當你要取信時,也需要知道自己的郵箱編號。計算機內存的工作原理就是這樣,每個數據都存儲在特定編號的"郵箱"裏。

2. 字節和位的概念

在深入瞭解數據存儲之前,我們需要理解兩個基本概念:位(bit)和字節(byte)。

位(bit)是計算機中最小的數據單位,它只能存儲0或1這兩個值。這就像一個開關,只有"開"和"關"兩種狀態。位的英文"bit"實際上是"binary digit"(二進制數字)的縮寫。

字節(byte)由8個位組成,是計算機中基本的存儲單位。一個字節可以存儲256種不同的值(從00000000到11111111,也就是十進制的0到255)。為什麼是8個位呢?這是歷史上形成的標準,8個位恰好可以表示一個英文字符。

3. 不同數據類型的存儲空間

不同的數據類型在內存中佔用的空間是不同的,這就像不同大小的物品需要不同大小的盒子來裝一樣。

字符型(char)

char類型通常佔用1個字節的空間。一個字節的8個位可以表示256種不同的值,這足夠表示所有的ASCII字符(包括大小寫字母、數字、標點符號等)。

想象一下,我們用一個小盒子來裝一個字符,這個盒子剛好夠放下一個字符,不多不少。

整型

不同的整型佔用不同的內存空間:

  • short通常佔用2個字節(16位),可以表示65536種不同的值。如果是有符號的,範圍是-32768到32767;如果是無符號的,範圍是0到65535。
  • int在現代系統中通常佔用4個字節(32位),可以表示約42億種不同的值。
  • long的大小取決於系統,在32位系統中通常是4個字節,在64位系統中通常是8個字節。
  • long long通常佔用8個字節(64位),可以表示非常大的數值範圍。

這就像我們有不同大小的盒子:小盒子裝小物品,大盒子裝大物品。如果我們知道要裝的物品不大,就不需要浪費空間使用大盒子。

浮點型

  • float通常佔用4個字節,按照IEEE 754標準的單精度格式存儲。
  • double通常佔用8個字節,按照IEEE 754標準的雙精度格式存儲。

浮點數的存儲比整數複雜得多,它分為三個部分:符號位、指數位和尾數位。這就像科學計數法一樣,比如3.14×10²,其中3.14是尾數,2是指數,符號是正號。

4. 數據的二進制表示

計算機內部所有數據都是以二進制形式存儲的,也就是隻用0和1來表示。這就像用莫爾斯電碼來傳遞信息一樣,只用"滴"和"嗒"兩種符號就能表示所有的文字。

整數的二進制表示

正整數的二進制表示比較直觀,就是將十進制數轉換為二進制數。比如:

  • 十進制的5在二進制中是101
  • 十進制的10在二進制中是1010

負整數的表示稍微複雜一些,大多數系統使用"二進制補碼"的方式。這種方式的好處是可以用同樣的電路來處理正數和負數的加法運算。

字符的二進制表示

字符是通過ASCII碼來轉換為數字,然後再轉換為二進制的。比如:

  • 字符'A'的ASCII碼是65,二進制是01000001
  • 字符'a'的ASCII碼是97,二進制是01100001
  • 字符'0'的ASCII碼是48,二進制是00110000

注意,字符'0'和數字0是不同的。字符'0'是一個顯示符號,它的ASCII碼是48;而數字0的二進制表示就是00000000。

5. 內存對齊的概念

在實際的內存存儲中,還有一個重要的概念叫做"內存對齊"。這是為了提高內存訪問效率而採用的策略。

想象一下,如果你要從書架上取書,整齊擺放的書比雜亂擺放的書更容易找到和取出。內存對齊就是這樣,它讓數據在內存中按照一定的規則整齊擺放。

<img src="https://lxlinux.superbed.verylink.top/item/68465eb958cb8da5c83bd7cd.png" style="zoom: 33%;" />

比如,一個int類型的變量通常要求存儲在4的倍數的地址上。如果有一個char變量佔用了地址1,那麼下一個int變量不會從地址2開始,而是從地址4開始,中間的地址2和3會被空出來。

這樣做雖然可能浪費一些內存空間,但可以大大提高數據訪問的速度。在結構體中,編譯器會自動進行內存對齊,有時候結構體的實際大小會比各個成員大小的總和要大。

6. 棧區和堆區的存儲

程序中的變量根據定義方式的不同,會被存儲在內存的不同區域:

棧區存儲

局部變量(在函數內部定義的變量)通常存儲在棧區。棧區就像一摞盤子,後放的盤子在上面,先拿走的也是上面的盤子,這叫做"後進先出"。

當函數被調用時,函數的局部變量會被"壓入"棧中;當函數結束時,這些變量會被自動"彈出"棧,內存空間會被自動回收。

堆區存儲

動態分配的內存(使用malloc等函數分配的內存)存儲在堆區。堆區的管理比棧區複雜,程序員需要手動申請和釋放內存。

全局區存儲

全局變量和靜態變量存儲在全局區,這些變量在程序運行期間一直存在。

2.1.3 字節序概念

1. 什麼是字節序?

字節序(Byte Order)是一個聽起來很技術化的概念,但實際上可以用一個很簡單的例子來理解。

想象一下,你要在紙上寫下數字"1234"。你會從左到右寫,先寫1,再寫2,然後3,最後4。但是,如果有些人習慣從右到左寫字,他們可能會先寫4,再寫3,然後2,最後1,最終在紙上呈現的可能是"4321"。

在計算機世界中,也存在類似的情況。當一個數據需要多個字節來存儲時,這些字節在內存中的排列順序就是字節序的問題。

2. 大端序與小端序

計算機世界中主要有兩種字節序:大端序(Big Endian)和小端序(Little Endian)。

大端序(Big Endian)

大端序的排列方式是高位字節存儲在低地址,低位字節存儲在高地址。這就像我們平常寫數字的習慣一樣,高位在前,低位在後。

舉個例子,十六進制數0x12345678在大端序的32位系統中會這樣存儲:

  • 地址1000: 0x12(最高位字節)
  • 地址1001: 0x34
  • 地址1002: 0x56
  • 地址1003: 0x78(最低位字節)

大端序的命名來源於《格列佛遊記》中的故事,在那個故事裏,有些人習慣從大頭(Big End)開始吃雞蛋。

小端序(Little Endian)

小端序的排列方式正好相反,低位字節存儲在低地址,高位字節存儲在高地址。這就像倒着寫數字一樣。

同樣的十六進制數0x12345678在小端序的32位系統中會這樣存儲:

  • 地址1000: 0x78(最低位字節)
  • 地址1001: 0x56
  • 地址1002: 0x34
  • 地址1003: 0x12(最高位字節)

小端序的命名也來源於《格列佛遊戲》,對應從小頭(Little End)開始吃雞蛋的人。

4. 為什麼會有不同的字節序?

你可能會想,為什麼要有兩種不同的字節序呢?直接統一成一種不是更好嗎?這其實有歷史原因和技術原因。

歷史原因

不同的計算機廠商在設計處理器時,基於不同的考慮選擇了不同的字節序。比如,Intel的x86系列處理器採用小端序,而Motorola的68000系列處理器採用大端序。隨着時間的推移,這些不同的選擇就固化下來了。

技術考慮

兩種字節序各有優勢:

大端序的優勢是比較直觀,符合人類的閲讀習慣。在網絡傳輸中,大端序被廣泛採用,所以也被稱為"網絡字節序"。

小端序的優勢是在進行某些數學運算時效率更高。比如,在進行類型轉換時,小端序系統可以直接使用低地址的數據,不需要重新計算地址。

5. 不同系統的字節序

常見系統的字節序

  • Intel x86/x64系列:小端序
  • ARM處理器:可配置,但通常使用小端序
  • PowerPC:大端序
  • SPARC:大端序
  • MIPS:可配置,可以是大端序或小端序

網絡字節序

在網絡通信中,為了保證不同系統之間能夠正確交換數據,規定統一使用大端序,這被稱為"網絡字節序"。當數據在網絡中傳輸時,發送方需要將數據轉換為網絡字節序,接收方再將數據轉換為本地字節序。

6. 字節序的影響

對程序員的影響

在大多數情況下,程序員不需要關心字節序問題,因為:

  1. 在同一台機器上運行的程序,字節序是一致的
  2. C語言的編譯器會自動處理大部分字節序問題
  3. 高級語言通常會屏蔽這些底層細節

但在某些情況下,字節序就變得很重要:

文件存儲

如果一個程序在小端序系統上創建了一個二進制文件,然後這個文件被傳輸到大端序系統上讀取,就可能出現數據錯誤。

比如,數字1234在小端序文件中可能存儲為D2 04(十六進制),但在大端序系統讀取時可能被解釋為1234(十六進制),這完全是錯誤的值。

網絡編程

在網絡編程中,經常需要在本地字節序和網絡字節序之間轉換。C語言提供了專門的函數來處理這種轉換:

  • htons():主機字節序轉網絡字節序(短整型)
  • htonl():主機字節序轉網絡字節序(長整型)
  • ntohs():網絡字節序轉主機字節序(短整型)
  • ntohl():網絡字節序轉主機字節序(長整型)

嵌入式系統

在嵌入式系統開發中,特別是當需要與其他系統通信或處理特定格式的數據時,字節序問題就變得很重要。程序員需要明確知道數據的字節序,並進行正確的處理。

7. 檢測系統字節序

我們可以用一個簡單的C程序來檢測當前系統的字節序:

#include <stdio.h>

int main() {
    int test = 1;
    char *p = (char*)&test;
    
    if (*p == 1) {
        printf("當前系統是小端序\n");
    } else {
        printf("當前系統是大端序\n");
    }
    
    return 0;
}

這個程序的工作原理是:整數1在內存中,如果是小端序,最低字節(值為1)會存儲在最低地址;如果是大端序,最低字節會存儲在最高地址。通過檢查最低地址的值,就可以判斷字節序。

8. 字節序轉換的實現

雖然系統提供了字節序轉換函數,但瞭解其實現原理也很有意義。以16位數據的字節序轉換為例:

unsigned short swap16(unsigned short value) {
    return ((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8);
}

這個函數通過位運算來交換高低字節的位置,從而實現字節序轉換。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.