void counter(void) {
static int count = 0; // 僅初始化一次
printf("%d ", ++count);
}
int main() {
for (int i = 0; i < 5; i++) counter(); // 輸出 1 2 3 4 5
}
static void helper(void) 限制該函數僅在本編譯單元可見,實現封裝,減少連結器符號汙染。
sizeof(int);對變數可省略:sizeof arr。sizeof 回傳指標大小,非陣列總大小。整型預設為 signed;char 是否有號由實作定義,跨平台時建議顯式指定。
絕對避免 float == 0.0。正確做法:fabs(x - target) < EPSILON。
優先處理正常流程,異常情況置於 else,減少巢狀層級,提升可讀性。
將迴圈次數最多的放在最內層,減少 CPU 切換迴圈層級的開銷,提升快取命中率。
void func(void):明確表示無參數(C89 中 func() 代表參數未指定)。void *:通用指標,可指向任意型別資料,需強制轉型後解參考。void a; 或 void func(void a)。| 宣告 | 指標可變? | 指向物件可變? | 口訣 |
|---|---|---|---|
const int *p | 是 | 否 | 從右讀:p 是指向 const int 的指標 |
int const *p | 是 | 否 | 同上,const 修飾左側型別 |
int *const p | 否 | 是 | p 是 const 指標,指向 int |
const int *const p | 否 | 否 | 雙重 const |
void print_str(const char *str); // 承諾不修改 str 指向的內容
僅宣告不定義,告訴編譯器「符號在別處定義」。標頭檔中對全域變數/函數使用 extern,源檔中定義一次。
public(C++ 中 class 預設 private)。#pragma pack 或屬性調整。union Data {
int i;
float f;
char bytes[4];
};
參見文章 1555 詳細說明。
常用於平台差異、功能開關、版本控制。
#if defined(__linux__)
#include
#elif defined(_WIN32)
#include
#else
#error "Unsupported platform"
#endif
// ❌ 危險
#define SQR(x) x * x
// SQR(10+1) -> 10+1*10+1 = 21
// ✅ 安全
#define SQR(x) ((x) * (x))
// SQR(10+1) -> ((10+1) * (10+1)) = 121
do { ... } while(0) 包裹,確保語句行為一致。int a[5]:a 是「陣列首元素地址」(右值),&a 是「整個陣列地址」(型別 int (*)[5])。a + 1 增加 sizeof(int);&a + 1 增加 sizeof(int[5])。a = p 錯誤)。// file1.c
char a[100];
// file2.c ❌ 錯誤
extern char *a; // 編譯器以為 a 是指標(4/8 bytes),實際是陣列(100 bytes)
// 存取 a[i] 會先讀取 a 的前 4/8 bytes 當作位址 -> 崩潰
// file2.c ✅ 正確
extern char a[]; // 或 extern char a[100];
根本原因: 陣列即地址,指標儲存地址。編譯器依聲明型別產生機器碼,聲明錯誤導致指令序列錯誤。
void func(int arr[]); // 等同於
void func(int *arr); // 等同於
void func(int arr[10]); // 10 被忽略,僅作文件用途
若需保留大小資訊,需額外傳遞長度參數,或使用指向陣列的指標:void func(int (*arr)[10])。
int *p; p++; 位址增加 sizeof(int),而非 1 byte。
C 語言看似簡單,實則暗藏精微。深入理解 static、const、extern 的儲存類別與連結屬性,掌握指標與陣列的本質區別,善用前處理器進行條件編譯與安全巨集定義,是從「能寫出跑得動的程式」進階到「寫出可維護、可移植、高效能系統程式」的必經之路。