本文系統梳理 C 語言進階開發必備的核心知識點,涵蓋記憶體模型、位運算、指針精髓、複雜表達式、聚合類型、巨集與預處理、函數庫機制、儲存類別與鏈結屬性、資料結構基礎等模組,旨在為開發者構建清晰的知識地圖。
記憶體晶片由多個 Bank 組成,每個 Bank 為行列陣列。容量計算:存儲單元數 = 行數 × 列數(位寬) × Bank 數。常用 M×W 表示規格,如 8M×8 表示 8M 單元、8 位寬,總容量 64 Mbit(8 MB)。
C 語言以 0 表示假、非 0 表示真,儲存時實際佔用 int 或 char 空間。C++ 才引入 bool。
為提高存取效率,編譯器預設 4 位元組對齊(32 位系統)或 8 位元組對齊(64 位系統)。結構體成員、結構體整體、首地址均需滿足對齊要求。可用 #pragma pack(n) 或 GCC 屬性 __attribute__((packed))、__attribute__((aligned(n))) 控制。
char/int/short/long/float/double 佔用不同位元組,同一組位模式可按不同型別解析(如 0000111010101 可作 int 或 float)。
C 語言是面向過程,但可在結構體內嵌函數指針實現封裝:
struct student {
int age;
char *name;
void (*eat)(void); // 函數指針
};
malloc/free。malloc 到 free 之間有效。malloc、calloc、realloc、free。分配後必檢查 NULL,釋放後指針置 NULL。| 區段 | 內容 | 特點 |
|---|---|---|
| 代碼段 | 二進制指令、字串常量、const 全域變量 | 唯讀,不可修改 |
| 資料段 | 已初始化非零全域/靜態區域變量 | 讀寫,程式啟動分配,結束釋放 |
| BSS 段 | 未初始化或初始化為 0 的全域/靜態變量 | 載入前不佔空間,啟動時清零 |
| 堆 | 動態分配記憶體 | 程式員手動管理,向上增長 |
| 棧 | 區域變量、函數參數、返回地址 | 編譯器自動管理,向下增長 |
const int a=9; 可能存放在代碼段(嵌入式)或資料段(編譯器判斷),具體視平台而定。
a.out(含代碼段、資料段、BSS、雜段)。.bin 燒錄,代碼/資料/BSS 連續,無堆、無 malloc。~(0u) 得到全 1 掩碼。&(與)、|(或)、~(非)、^(異或)。1 進行置位、清位、取反、移位。#define SET_NTH_BIT(x, n) (x | ((1U) << (n-1)))
#define CLEAR_NTH_BIT(x, n) (x & ~((1U) << (n-1)))
%p 輸出指針地址。int *p1, p2; 僅 p1 是指針,p2 是 int。NULL,使用前檢查;free 後置 NULL。NULL 定義差異:C++ 為 0,C 為 (void*)0。const 修飾指針四種形式:const int *p、int const *p(指向常量)、int * const p(常量指針)、const int * const p(雙重常量)。a 是首元素地址,&a 是整個陣列地址(陣列指針),值相同但類型不同。sizeof 是運算元(編譯期計算大小),strlen 是函數(運行期尋找 \0)。int (*p)(int, int); p = max; p(); 等同 max()。函數指針陣列 int (*p[4])(int, int);。printf 行緩衝,遇 \n 刷新。\r 回車,\n 換行,Windows \r\n,Unix \n,Mac \r。#ifndef _GUARD_ #define _GUARD_ ... #endif。typedef 別名:typedef int *intType; const intType p; 表示 p 為常量指針。跨平台移植常用 int32_t、int16_t 等。int (*p)[5] ↔ 二維陣列 int a[2][5]int *p; 定義指針;int (*p)[4]; 定義指向 4 元素陣列的指針;int (*p)(int, int); 定義函數指針。見到 (*p) 即為指針變量。char a[]={'l','i','n','u','x','\0'}、char a[]="linux"、char *p="linux"(指向唯讀區)。sizeof 含 \0,strlen 不含。指針 sizeof 永遠是指針大小(4/8 位元組)。. 或 -> 本質是指針運算:s1.a 等價 *(int*)&s1,s1.b 需按位移計算地址。offsetof 宏計算成員偏移:#define offsetof(TYPE, MEMBER) ((int)&(((TYPE*)0)->MEMBER))。container_of 宏:從成員指針反推結構體指針,Linux 核心鏈表核心技術。typeof 取得變量類型。(int)p+6 按 int 大小跳躍,(char*)p+6 按位元組,(int*)p+6 相當於 p+24。char* 觀察 int a=1 低位元組。int,預設從 0 遞增,可手動指定值。同編譯單位內枚舉常量名不可重複,typedef 別名也不可衝突。.c → (預處理) → .i → (編譯) → .S → (彙編) → .o → (鏈接) → ELF 可執行檔
工具鏈:預處理器、編譯器、彙編器、鏈接器 → GCC 整合所有。
#include、註釋、條件編譯、巨集展開。-E 僅預處理、-c 僅編譯、-o 指定輸出、-I 指定頭文件路徑。strip 可剝離生成 Release 版,objcopy 生成 .bin 燒錄映像。#include "xxx.h":當前目錄優先,再搜尋系統路徑。#include <xxx.h>:僅搜尋系統路徑。-I 指定額外路徑。#ifdef DEBUG #define debug(x) printf(x) #else #define debug(x) #endif 實現調試開關。#define YEAR (365*24*60*60UL) 需加 UL 避免溢位。#define MAX(a,b) ((a)>(b)?(a):(b))。inline:結合兩者優點,適合短小函數。| 特性 | 靜態庫 (.a) | 動態庫 (.so) |
|---|---|---|
| 鏈接時機 | 編譯鏈接期,代碼複製進可執行檔 | 運行期,僅記錄符號引用 |
| 記憶體佔用 | 多進程各自擁有副本,佔較大 | 多進程共享同一份記憶體 |
| 更新部署 | 需重新編譯鏈接 | 替換 .so 即可,無需重編應用 |
| GCC 預設 | 需 -static 強制 | 預設動態鏈接 |
gcc -c src.c -o src.o; ar -rc libname.a src.o(前綴必為 lib)。gcc -fPIC -c src.c -o src.o; gcc -shared -o libname.so src.o。-lname -L/path -I/path。運行時需設定 LD_LIBRARY_PATH 或複製到 /usr/lib。ldd 檢查依賴。string.h:memcpy(不重疊高效)、memmove(重疊安全)、memset、strcpy、strcat、strcmp 等。math.h:需鏈接 -lm。鏈接器預設僅搜尋常用庫,非常用庫需顯式 -lxxx。| 關鍵字 | 用途 | 儲存區 | 作用域 | 生命週期 | 鏈結屬性 |
|---|---|---|---|---|---|
auto | 區域變量(可省略) | 棧 | 塊內 | 函數調用期間 | 無 |
static | 1.區域靜態 2.全域/函數限定本檔 | 資料段/BSS | 1.塊內 2.檔案級 | 程序全程 | 1.無 2.內部 |
register | 建議放入寄存器 | 寄存器/棧 | 塊內 | 函數調用期間 | 無 |
extern | 聲明外部全域變量/函數 | 資料段/BSS | 全程序(跨檔) | 程序全程 | 外部 |
volatile | 禁止編譯器優化,硬體/中斷/多執行緒可見 | 視原定義 | 視原定義 | 視原定義 | 視原定義 |
restrict | C99,指針獨佔訪問,允許積極優化 | 視原定義 | 視原定義 | 視原定義 | 視原定義 |
volatile 典型場景:中斷服務程式修改變量、多執行緒共享變量、硬體寄存器映射。寧可誤加,不可漏加。restrict 用於 memcpy 等標準庫原型,告知編譯器源/目標不重疊。資源管理者(CPU、記憶體、硬體),提供 API 供應用程式申請服務。裸機適合極簡產品,複雜產品必上 OS。
內核 API 面向功能完備性,不顧易用性;C 庫封裝 API,優化介面(如 fopen 封裝 open)。純用戶態函數(strcpy、atoi)無需 API。
不同 OS API 不同,但 C 庫介面相似(人為屏蔽差異)。跨平台框架:Qt、Java。
main 函數深度解析int main(int argc, char **argv) 或 int main(int argc, char *argv[])(二者等價)。fork/exec 創建子進程執行程式。Shell 執行 ./a.out 實為 Shell 進程 fork 子進程。argc 含程式名(argv[0]),實際參數從 argv[1] 開始。void 型別本質「未知型別」而非「空型別」。void* 是通用指針,指向未知類型對象;void 不能定義變量。C 是強類型語言,所有變量必有明確類型以確定大小與解析方式。
NULL 與相關概念辨析| 符號 | 含義 | 內存值 | 佔用空間 |
|---|---|---|---|
'\0' | 字串結束符 | 0 | 1 位元組 |
'0' | 字符 '0' | 48 (ASCII) | 1 位元組 |
0 | 整數 0 | 0 | int 大小 |
NULL | (void*)0,空指針 | 0 (地址) | 指針大小 |
規範用法:int *p = NULL; if (p != NULL) *p = ...;
複雜表達式中編譯器隱式創建臨時變量保存中間結果(如 (int)a、b/3),用完即銷毀。
#define debug(x) printf(x) 配合 DEBUG 開關。#define DBG(...) fprintf(stderr, "DBG(%s, %s(), %d): ", __FILE__, __FUNCTION__, __LINE__); fprintf(stderr, __VA_ARGS__) 含檔案、函數、行號、變參、錯誤流輸出(重定向不影響)。#define DEBUG(fmt, args...) printk(fmt, ##args)。next 指針。必須用堆記憶體(malloc),使用前清理髒數據。**head)或返回新頭指針。scanf 讀取結構體成員需取地址 &(s->age)。char name[10],避免指向唯讀區導致段錯誤。free() 釋放記憶體。prev、curr、next)。struct list_head { struct list_head *next, *prev; }; 嵌入業務結構體,配合 container_of 宏存取數據。list_add 等無雙下劃線介面,雙下劃線為內部實現勿用。while(1) { switch(state) { case S1: ...; if(input) state=S2; break; ... } }。char 相加自動提升為 int。signed char (-128~127) vs unsigned char (0~255),記憶體位模式相同,解析不同。ldr r0, 0x12345678),運行時需重定位。printf 可變參數實現依賴棧幀佈局與 va_list 機制。printf、malloc),但裸機無標準輸出設備、無堆定義,部分需移植或重寫。