作用:告訴編譯器該變數可能隨時被外部改變(如中斷、硬體暠存器、多執行緒共享旗標),禁止優化,每次存取必須從記憶體讀寫。
適用場景:
應用層:SMTP、FTP、Telnet 等應用程式協議。
傳輸層:TCP(可靠、流控、擁塞控制,表頭 20+ 位元組)、UDP(不可靠、無連線、表頭 8 位元組、低延遲)。
網路層:IP、ICMP、ARP、RARP。
資料鏈路層:Ethernet、PPP。
物理層:光纖、雙絞線等傳輸媒介。
預處理(預編譯)處理 # 開頭指令:檔案包含(#include)、巨集替換(#define)、條件編譯(#if/#ifdef)。輸出為無巨集、無註解的純 C 代碼,再交由編譯器編譯。
public;class 預設為 private。將絕對地址強轉為函式指標並呼叫:
typedef void (*voidFuncPtr)();
*((voidFuncPtr)0x100000)();
// 或
*((void (*)())0x100000)();
常用於嵌入式系統呼叫固定地址的 Bootloader 或中斷向量。
void GetMemory(char **p, int num) { *p = (char*)malloc(num); }
int main() {
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
free(str); // 釋放記憶體
if (str != NULL) // 錯誤:str 指標值未變,仍指向已釋放區塊
strcpy(str, "world"); // 未定義行為
printf("%s", str);
}
正確做法:free(str); str = NULL; 避免野指標。
16 位機上 60*60*24*365 會溢位,需加 L 或 UL 強制長整型運算:
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
利用巨集將絕對地址映射為 volatile 指標:
#define rWTCON (*(volatile unsigned *)0x53000000)
unsigned int a = 6;
int b = -20;
(a + b > 6) ? puts("> 6") : puts("<= 6"); // a+b 經整數提升為 unsigned,結果極大,輸出 "> 6"
避免 x == 0.0,改用誤差範圍:
#define EPSINON 1e-9
if ((x >= -EPSINON) && (x <= EPSINON)) // 視為零
#include <assert.h> void assert(int expression); 僅在 Debug 建置生效,Release 時被移除。
char *GetMemory() {
char p[] = "hello world"; // 區域陣列,棧上分配
return p; // 錯誤:函式結束後記憶體失效
}
正確做法:動態分配 malloc、靜態陣列、或呼叫端提供緩衝區。
Brian Kernighan 演算法(每次清除最低位 1):
int countBits(unsigned int n) {
int c = 0;
for (; n; ++c) n &= (n - 1);
return c;
}
查表法(256 字節預計算表,適合 32/64 位整數):
unsigned char BitsSetTable256[256];
void initTable() {
for (int i = 0; i < 256; ++i)
BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2];
}
int countBitsFast(unsigned int n) {
unsigned char *p = (unsigned char*)&n;
return BitsSetTable256[p[0]] + BitsSetTable256[p[1]] +
BitsSetTable256[p[2]] + BitsSetTable256[p[3]];
}
| 特性 | typedef | #define |
|---|---|---|
| 處理階段 | 編譯期 | 預處理期 |
| 型別檢查 | 有 | 無(純文字替換) |
| 作用域 | 有(遵循區塊作用域) | 無(全域,直到 #undef) |
| 用途 | 型別別名 | 巨集常數、程式碼片段 |