指針強制轉換是 C 語言靈活性與危險性的集中體現。本文通過兩個經典案例——**printf 變參訪問**與**整型拆解字節**——深入剖析轉換規則、內存佈局與實戰陷阱。
int printf(const char *fmt, ...) 中,fmt 是 char*(32 位系統佔 4 字節)。
(char *)&fmt // fmt 的地址 → char*:按字節訪問 fmt 自身的 4 字節
(char *)&fmt + 4 // 跳過 fmt 自身,指向棧上下一個參數(var3)
(int *)((char *)&fmt + 4) // 轉為 int*:按 4 字節讀取 var3
*((int *)((char *)&fmt + 4)) // 解引用得到 var3 的值
| 指針類型 | 指針算術 +1 移動 | 解引用訪問大小 |
|---|---|---|
char * | 1 字節 | 1 字節 |
int * | 4 字節 | 4 字節 |
double * | 8 字節 | 8 字節 |
無論什麼類型指針,**相減結果皆為元素個數差**,而非字節差。
int a = 6336; // 0x18C0 (小端序:C0 18 00 00)
char *b = (char *)&a; // 強制轉為 char*
printf("%d\n", *b); // 輸出 -64 (即 0xC0 有符號解釋)
地址: &a &a+1 &a+2 &a+3
值 : 0xC0 0x18 0x00 0x00
↑
b (char*) 只讀取第一字節 0xC0 = -64 (signed char)
int a = 6336;
unsigned char *p = (unsigned char *)&a; // 用 unsigned 避免符號擴展
for (int i = 0; i < sizeof(int); ++i)
printf("Byte %d: 0x%02X (%d)\n", i, p[i], p[i]);
/* 輸出:
Byte 0: 0xC0 (192)
Byte 1: 0x18 (24)
Byte 2: 0x00 (0)
Byte 3: 0x00 (0)
*/
char *p = (char *)&int_var;(編譯器會警告)。malloc 返回 void*,使用前必須轉型。va_arg(ap, int) 內部本質也是轉換。volatile uint32_t *reg = (uint32_t *)0x40000000;。char* 逐字節收發。| 陷阱 | 後果 | 對策 |
|---|---|---|
| 違反嚴格別名規則 | 編譯器優化導致邏輯錯誤 | 用 union 或 memcpy 代替直接轉換 |
| 對齊要求不滿足 | 硬件異常(SIGBUS)或性能下降 | 確保目標地址滿足類型對齊(如 4 字節對齊) |
| 符號擴展 | char 有符號導致負數 | 用 unsigned char / uint8_t 處理原始字節 |
| 大小端假設 | 跨平台數據錯誤 | 序列化時顯式處理字節序(htonl/ntohl) |
union、memcpy、標準庫 stdint.h 類型。// 故意違反嚴格別名,為了變參訪問。-Wstrict-aliasing、-fstrict-aliasing、clang-tidy 檢查。指針強制轉換本質是**重新解讀內存位模式**。掌握「指針類型決定步長與訪問寬度」、理解小端序佈局、遵守嚴格別名規則,才能在系統編程、協議解析、嵌入式開發中安全、高效地使用這把雙刃劍。