需將兩個 C 字符串 s="abc"、t="def" 安全拼接為新字符串 r。核心挑戰:目標緩衝區地址有效性、容量充足性、所有權清晰、空終止符處理。
char* r;
strcpy(r, s); // r 未初始化,寫入隨機地址 -> 崩潰/安全漏洞
strcat(r, t);
致命傷:r 未分配內存,行為未定義。
char r[100]; // 編譯期確定大小
strcpy(r, s);
strcat(r, t);
優點:無動態分配開銷,自動釋放。
缺點:若 strlen(s)+strlen(t) >= 100 發生緩衝區溢出;大數組消耗棧空間(嵌入式環境受限)。
char *r = malloc(strlen(s) + strlen(t)); // 少分配 1 字節
strcpy(r, s);
strcat(r, t); // 寫入 \0 時越界
錯誤:strlen 不計算結尾 \0,拼接後需 +1 空間存放終止符。
char *r = malloc(strlen(s) + strlen(t) + 1); // +1 給 \0
if (!r) { perror("malloc failed"); exit(1); }
strcpy(r, s);
strcat(r, t);
printf("%s\n", r);
free(r); // 必須釋放,避免洩漏
關鍵點:
1. 分配前檢查指針非空。
2. 使用 strcpy/strcat 而非 sprintf 避免格式化開銷。
3. 用畢 free,並將指針置 NULL 防雙重釋放。
C11 標準庫(Annex K)提供邊界檢查版本:
// 需定義 __STDC_WANT_LIB_EXT1__ 1
strcpy_s(r, size, s);
strcat_s(r, size, t);
或使用 POSIX asprintf/vasprintf 自動分配:
char *r;
asprintf(&r, "%s%s", s, t); // 內部 malloc,需 free(r)
在 C++ 中優先使用 std::string;純 C 項目可定義結構體封裝:
typedef struct { char* data; size_t len; size_t cap; } String;
void string_init(String* s);
void string_append(String* s, const char* src);
void string_free(String* s); // 內部 free(data); s->data=NULL;
統一內存管理入口,消除手動 malloc/free 分散帶來的洩漏風險。
字符串拼接看似微小,實則考驗內存模型理解。原則:長度不可預測必用動態分配、永遠預留 \0 空間、分配必檢查、用畢必釋放、封裝所有權語義。 將方案四內化為標準模板,配合靜態分析工具檢查 malloc/free 配對,構建零洩漏代碼庫。