看似簡單的 strcpy、指標傳遞、動態記憶體操作,實則暗藏細節。本文以面試真題形式,層層遞進從 2 分寫法進化到 10 分工業級實作,並揭示 7 大記憶體管理經典錯誤。
void strcpy(char* dest, char* src) {
while ((*dest++ = *src++) != '\0');
}
void strcpy(char* dest, const char* src) { ... }
void strcpy(char* dest, const char* src) {
assert(dest && src);
while ((*dest++ = *src++) != '\0');
}
char* strcpy(char* dest, const char* src) {
assert(dest && src);
char* ret = dest;
while ((*dest++ = *src++) != '\0');
return ret;
}
關鍵點:const 語意、assert 防禦、回傳目的地址支援 strcat(strcpy(buf, a), b) 鏈式風格。
void test3(char* src) {
char buf[10];
if (strlen(src) <= 10) // 錯!strlen 不含 '\0'
strcpy(buf, src); // src 長度 10 時會溢位
}
// 正確:if (strlen(src) < sizeof(buf))
| 編號 | 錯誤代碼 | 核心問題 | 修正要點 |
|---|---|---|---|
| 4 | void Get(char* p){ p=malloc(100); } | 指標按值傳遞,呼叫端指標不變 | 傳 char** 或回傳指標 |
| 5 | char* Get(){ char p[]="hi"; return p; } | 回傳區域陣列地址(棧上),離開作用域失效 | 動態配置或靜態/全域儲存 |
| 6 | *p = malloc(n); 未檢查 NULL | 配置失敗時解引用野指標 | if(!*p) handle_error(); |
| 7 | free(p); 未置 NULL | 產生野指標,後續誤用 | free(p); p=NULL; |
| 6/7 | 未釋放動態記憶體 | 記憶體洩漏 | 配置釋放配對,RAII 管理 |
| - | swap(int* a,int* b){ int* p; *p=*a; ... } | p 未初始化(野指標) | 用 int tmp 而非指標 |
| - | 指標運算越界 | 緩衝區溢位 | 界檢查、使用 std::span/容器 |
| 類型 | 推薦寫法 | 理由 |
|---|---|---|
| BOOL | if (!flag) | 邏輯判斷語意清晰 |
| 整數 | if (val == 0) | 數值比較,避免隱性轉換 |
| 浮點數 | if (fabs(x) <= EPS) | 浮點不精確,不可用 == |
| 指標 | if (ptr == nullptr) | 語意明確,非邏輯非 |
void Func(char str[100]) { sizeof(str); } // 4 (指標大小)
void* p = malloc(100); sizeof(p); // 4 (指標大小)
// 陣列退化為指標,sizeof 失去元素數量資訊
char s[10]; sizeof(s) == 10s++ 編譯錯誤(非左值)