a.out,指定輸出:gcc -o myapp main.cprintf("FILE=%s, LINE=%d, FUNC=%s\n", __FILE__, __LINE__, __func__);
-Wall -Wextra -Wpedantic -std=c11 -g -O2| 類別 | 關鍵字 |
|---|---|
| 數據類型 (5) | void char int float double |
| 類型修飾 (4) | short long signed unsigned |
| 復雜類型 (5) | struct union enum typedef sizeof |
| 存儲類別 (6) | auto static register extern const volatile |
| 流程控制 (4) | return continue break goto |
| 分支結構 (5) | if else switch case default |
| 循環結構 (3) | for do while |
| C99 新增 (5) | inline restrict _Bool _Complex _Imaginary |
| C11 新增 (7) | _Alignas _Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local |
| 類型 | 32位系統 | 64位系統 (LP64) | 備註 |
|---|---|---|---|
char | 1 | 1 | 最小寻址單元 |
short | 2 | 2 | 通常 16 位 |
int | 4 | 4 | 自然字長 |
long | 4 | 8 | LP64 模型關鍵差異 |
long long | 8 | 8 | C99 保證 ≥64 位 |
指針 | 4 | 8 | 地址總線寬度/8 |
float | 4 | 4 | IEEE 754 單精度 |
double | 8 | 8 | IEEE 754 雙精度 |
工程鐵律:跨平台代碼必須使用 <stdint.h> 精確寬度類型(int32_t、uintptr_t、intmax_t 等)。
double ← float ↑ long double (若存在) ↑ unsigned long long ← long long ↑ unsigned long ← long ↑ unsigned int ← int ↑ unsigned short ← short ↑ char (signed/unsigned) → int
unsigned int + int → 無符號運算,負數變巨大正數0 開頭(如 077 = 63)0x/0X 開頭(如 0xFF = 255)0b/0B 開頭(GCC/Clang 擴展,C23 標準化)三步法:
-7 (int32):
7 = 0x00000007
~7 = 0xFFFFFFF8
+1 = 0xFFFFFFF9 // 存儲值
printf("%#x\n", -7); // 輸出 0xfffffff9
| 類型 | 符號位 | 指數位 | 尾數位 | 偏移量 |
|---|---|---|---|---|
float | 1 | 8 | 23 | 127 |
double | 1 | 11 | 52 | 1023 |
示例 8.25 (float):
1000.01 = 1.00001 × 233 + 127 = 130 = 1000001000001 + 後補 0 至 23 位0 10000010 00001000000000000000000 = 0x41040000負數僅翻轉符號位:-8.25 = 0xC1040000
<< 左移:低位補 0,等價乘 2n(無符號保證,有符號溢出 UB)>> 右移:
-7 (0xFFFFFFF9) >> 4 = 0xFFFFFFF9 (算術右移,符號位擴展) 1000 (0x3E8) >> 4 = 0x3E (邏輯右移)
| 運算符 | 行為 | 陷阱 |
|---|---|---|
&& / || | 短路求值 | 右側表達式可能不執行(副作用丟失) |
^ | 異或 | a^a=0、a^0=a、交換變量 a^=b^=a^=b (不可讀,勿用) |
?: | 三目 | 類型為兩操作數通用類型,副作用僅執行一次 |
, | 逗號 | 左到右求值,整個表達式值為最右子表達式值 |
sizeof | 編譯期計算 | 不求值表達式(sizeof(i++) 不會增 i) |
printf 格式化輸出精要printf("%d,%d\n", i++, i); → 求值:右邊 i=10,左邊 i++=10 → 輸出 10,10printf("%d,%d\n", i, i++); → 求值:右邊 i++=10 (i 變 11),左邊 i=11 → 輸出 11,10%d/%i 有符號十進制,%u 無符號,%x/%X 十六進制,%o 八進制,%f/%e/%g 浮點,%c 字符,%s 字符串,%p 指針,%% 字面 %%5d 寬度,%.3f 精度,%-10s 左對齊,%+d 強制符號,%08x 補零,%#x 前綴 0xif-else if-else、switch-case-default(表達式必為整型/字符型)while:先判斷後執行do-while:至少執行一次for(init; cond; step):init→cond→body→step→cond...跳轉:break 跳出當前循環/switch,continue 進入下一次迭代,return 退出函數,goto 標簽跳轉(慎用)。
| 類別 | 存儲期 | 作用域 | 鏈接性 |
|---|---|---|---|
| 普通局部 | 塊執行期間 | 塊內 | 無 |
| 普通全局 | 程序全期 | 文件內(外部可見) | 外部 |
靜態局部 (static) | 程序全期 | 塊內 | 無 |
靜態全局 (static) | 程序全期 | 文件內(內部鏈接) | 內部 |
extern 全局 | 程序全期 | 全程序(需聲明) | 外部 |
{ int a=10; } 遮蔽外層 astatic 修飾函數:僅本文件可見(內部鏈接)int a = 10; int *p = &a; // 取地址 int val = *p; // 解引用讀取 *p = 20; // 解引用寫入 (a 變 20)
int *p:指針類型 int*,指向類型 intsizeof(指向類型)NULL (或 0、(void*)0) 表示「不指向有效對象」if (!p) 優於 if (p == NULL)int arr[10]; int *p = &arr[0], *q = &arr[8]; p + 1; // 向後偏移 1 個 int (4 字節) p - q; // 相差元素個數 (有符號,這裡為 -8) (*p)++; // arr[0] 值加 1,指針不變 *p++; // 取 *p 值,然後 p 移向下一元素 *++p; // p 先移動,再取值
void* 通用指針*(int*)pchar *s = "hello"; // s 指向只讀區,不可修改 (*s='H' 崩潰) char s[] = "hello"; // s 在棧上,可修改 (s[0]='H' 合法)
int a=10, *p=&a, **q=&p; // q 指向 p,**q 即 a
char *argv[] = {"cmd", "arg1"}; // 指針數組
char **p = argv; // 指向指針數組首元素
int a[3][4]; int (*p)[4] = a; // p 指向含 4 個 int 的數組 (即一行) p+1; // 跳過一行 (16 字節)
| 函數 | 原型 | 特性 |
|---|---|---|
malloc | void* malloc(size_t size) | 分配 size 字節,不初始化,失敗返 NULL |
calloc | void* calloc(size_t n, size_t size) | 分配 n*size,歸零初始化,防溢出檢查 |
realloc | void* realloc(void* ptr, size_t size) | 調整大小,可能移動內存,ptr 為 NULL 等同 malloc |
free | void free(void* ptr) | 釋放,ptr 為 NULL 安全無操作,雙重釋放 UB |
模式:int *p = malloc(n * sizeof *p); if (!p) handle_err(); ... free(p); p = NULL;
const 與 volatile 進階const int *p; // p 指向常量,不可 *p=,可 p= int *const p; // p 是常指針,不可 p=,可 *p= const int *const p; // 雙重 const // 函數參數 void read_only(const int *p); // 承諾不修改 *p void mutate(int *const p); // 承諾不改 p 指向 (少用)
const:只讀段,硬件保護,不可通過指針修改const:棧上,編譯器優化為常量,但可通過指針修改 (僅有警告)volatile:禁止優化,每次訪問真實內存/寄存器,用於硬件寄存器、中斷共享變量、信號處理void f(int a[]) 等價 void f(int *a),sizeof(a) 為指針大小sizeof 陷阱int a = 10;
int arr[10] = {1,2,3};
char *s = "hello";
sizeof(a); // 4 (int)
sizeof(arr); // 40 (10*4)
sizeof(s); // 4/8 (指針大小),非字符串長度
strlen(s); // 5 (字符串長度,不含 \0)
// 函數參數退化
void f(int a[100]) { sizeof(a); } // 4/8,a 是指針
atoi/atol/atoll:簡單轉換,錯誤返回 0,無法區分 "0" 和錯誤strtol/strtoul/strtod:健壯解析,支持進制、尾指針返回、錯誤檢測 (errno)sprintf/snprintf:格式化輸出到字符串,snprintf 防溢出sscanf:從字符串解析,返回成功匹配項數#define REG32(addr) (*(volatile uint32_t*)(addr)) REG32(0x40020000) = 0x1; // 寫寄存器 uint32_t val = REG32(0x40020004); // 讀寄存器
這份手冊提煉了 C 語言工程實踐中最高頻、最高價、最易錯的核心知識點。建議收藏,遇到疑難雜癥時作為快速參考;更建議通讀 C11 標準草案 (N1570) 與 《C 專家編程》,建立系統性知識框架。