#include
#include
#include
void getmemory(char *p) { // 參數是指標**副本**
p = (char *)malloc(100); // 修改的是副本,外部 str 不受影響
}
int main() {
char *str = NULL;
getmemory(str); // 傳遞 str 的值 (NULL)
strcpy(str, "hello world"); // str 仍為 NULL -> 段錯誤
printf("%s\n", str);
free(str);
return 0;
}
結果:程式崩潰 (Segmentation fault)。getmemory 內的 malloc 分配的記憶體位址賦值給參數 p (區域變數),函數返回後 p 銷毀,外部 str 依然是 NULL,且分配的記憶體洩漏。
void getmemory(char *p) 中,p 是指標變數,**按值傳遞**。呼叫時將 str 的值 (位址或 NULL) 複製給 p。函數內修改 p 指向新位址,不影響 str。
類比:str 是一張寫著地址的紙條。呼叫函數時,你把地址抄寫在另一張紙條 p 上給函數。函數在 p 上塗改新地址,你的原紙條 str 當然不變。
void getmemory(char **p) { // 接收指標的位址
*p = (char *)malloc(100); // 解參考修改原指標指向
}
int main() {
char *str = NULL;
getmemory(&str); // 傳遞 str 的位址
strcpy(str, "hello world");
printf("%s\n", str); // 正常輸出
free(str);
return 0;
}
原理:&str 是 char**,函數透過 *p 間接修改 str 的值。
char *getmemory(void) { // 回傳分配的位址
char *p = (char *)malloc(100);
return p; // 將位址回傳給呼叫端
}
int main() {
char *str = getmemory(); // 直接接收回傳值
strcpy(str, "hello world");
printf("%s\n", str);
free(str);
return 0;
}
優點:語意清晰「取得記憶體」、呼叫端明確擁有權、易於錯誤處理 (回傳 NULL 表示失敗)。
char *getmemory(void) {
static char buf[100]; // 靜態儲存期,生命週期至程式結束
return buf;
}
注意:非執行緒安全、不可重入、大小固定。僅適合簡單工具程式或單執行緒環境。
typedef struct {
char *data;
size_t size;
} Buffer;
bool buffer_init(Buffer *b, size_t size) {
b->data = malloc(size);
b->size = b->data ? size : 0;
return b->data != NULL;
}
void buffer_free(Buffer *b) {
free(b->data);
b->data = NULL;
b->size = 0;
}
// 使用
Buffer buf;
if (buffer_init(&buf, 100)) {
strcpy(buf.data, "hello");
printf("%s\n", buf.data);
buffer_free(&buf);
}
將指標與大小綁定,提供初始化/釋放配對函數,避免洩漏、雙重釋放、野指標。
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
void f(int *p) { p = &x; } | 修改區域指標副本 | 傳 int** 或回傳 int* |
void f(char *s) { s = "lit"; } | 指向字面常數,外部指標未變 | 用 strcpy 複製內容,或回傳 const char* |
void f(int a[]) { a = malloc(...); } | 陣列參數退化為指標,同第一類 | 同方法 1 或 2 |
char *p; f(&p); // f 參數 void f(char *p) | 類型不匹配 (char** vs char*) | 函數參數改 char** |
T* create_T(),失敗回傳 NULL,呼叫端 if (!(p = create_T())) handle_error();bool get_T(T **out),需修改呼叫端指標時void read_only(const T *p) 承諾不修改 *p;void modify(T *const p) 承諾不修改 p (指標本身)-Wall -Wextra)、使用 cppcheck、clang-tidy 捕捉指標誤用C 語言指標傳遞本質是「值傳遞」。要在函數內修改外部指標指向,必須傳遞指標的位址 (二級指標) 或回傳新指標。理解這一點,能避開 90% 的指標相關 Bug。現代 C 風格傾向回傳指標而非輸出參數,語意更清晰、組合性更好。