C語言角落裡藏著幾個強大但常被忽視的特性。本文深度解析 const 編譯器優化與 typedef 交互、volatile 記憶體屏障語義、C99 柔性陣列成員機制、編譯器詞法分析的「貪婪法則」、實用預處理指令(#line, #error)與預定義宏,助你駕馭這些進階武器。
const:不只是「只讀」const int MAX = 100; 編譯器通常**不分配存儲空間**,直接置入符號表,作為編譯期常量傳播,消除記憶體讀取,效率等同 #define MAX 100。const 有**型別**、**作用域**、**地址**(若取址則會分配存儲),除錯器可見;宏無型別、文本替換、多份拷貝。const + typedef 陷阱
typedef struct Student *StuPtr;
const StuPtr p1; // 等價 StuPtr const p1 → struct Student *const p1 (常量指針)
StuPtr const p2; // 同上
規則:const 修飾的是**變數本身**(指針變數),而非 typedef 別名代表的型別。typedef **不進行文本展開**,而是被視為一個不可分割的型別標識符。
volatile:告訴編譯器「這裡有外部魔法」volatile uint32_t *GPIO_ODR = (uint32_t*)0x40020014; 硬體寄存器映射。volatile **不提供原子性、不提供記憶體屏障、不保證執行緒可見性**!多執行緒同步請用 atomic (C11) / pthread_mutex / 平台原子操作。
struct Line {
int length;
char contents[]; // 無長度陣列,必須是結構體**最後一個**成員
};
sizeof(struct Line) == sizeof(int))。struct Line *p = malloc(sizeof(struct Line) + n);。
struct Line90 { int length; char contents[0]; }; // GCC/Clang 支援零長度陣列
struct Line90_alt { int length; char contents[1]; }; // 經典「結構體黑客」,需手動 -1 分配
a+++b 如何解析?a+++b → a ++ + b(a++ 合法,a+++ 非法)→ (a++) + b。a---b → a -- - b → (a--) - b。(a++) + b。| 指令 | 作用 | 範例 |
|---|---|---|
#line | 改變編譯器報告的行號/檔名 | #line 100 "my_gen.c" 生成代碼調試用 |
#error | 強制編譯錯誤並輸出訊息 | #error "Unsupported platform!" |
#pragma | 編譯器特定指示(對齊、優化、訊息抑制) | #pragma pack(1) #pragma GCC diagnostic ignored "-Wunused" |
__LINE__ // 當前行號 (int)
__FILE__ // 當前檔名 (string literal)
__DATE__ // 編譯日期 "Mmm dd yyyy"
__TIME__ // 編譯時間 "hh:mm:ss"
__STDC__ // 是否符合標準 C (1/0)
__STDC_VERSION__ // C 版本: 199409L(C90), 199901L(C99), 201112L(C11), 201710L(C18)
__func__ // 當前函數名 (C99, 靜態字串)
應用:日誌宏 #define LOG(fmt,...) printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)。
掌握 const 編譯期常量傳播與 typedef 交互規則、volatile 硬體/並發邊界語義、柔性陣列動態結構體模式、貪婪法則避坑、預處理宏診斷能力,能讓你在嵌入式、系統級、高效能 C 開發中遊刃有餘。