char *strcat(char *restrict dest, const char *restrict src);
char *strncat(char *restrict dest, const char *restrict src, size_t n);
| 特性 | strcat | strncat |
|---|---|---|
| 連接長度 | src 全長 (含結束符) | 最多 n 字元 (不含結束符) |
| 結束符處理 | 覆蓋 dest 結束符,複製 src 結束符 | 若 n < src長:複製 n 字元後自動添加結束符 若 n >= src長:行為同 strcat |
| 返回值 | dest | dest |
| 緩衝區溢出風險 | 極高 (無長度檢查) | 可控 (需 dest 空間 ≥ strlen(dest) + n + 1) |
// strcat 等價實現
char *strcat(char *dest, const char *src) {
char *d = dest;
while (*d) d++; // 定位 dest 結束符
while ((*d++ = *src++)); // 複製 src (含結束符)
return dest;
}
// strncat 等價實現
char *strncat(char *dest, const char *src, size_t n) {
char *d = dest;
while (*d) d++; // 定位 dest 結束符
while (n-- && (*d++ = *src++)); // 複製最多 n 字元
*d = '\0'; // 關鍵:強制添加結束符
return dest;
}
int main() {
char a[20] = "Hello";
char b[] = "World";
char c[] = "!!!";
printf("Initial: a=[%s]\n", a); // Hello
strncat(a, b, 3); // 連接 "Wor"
printf("strncat 3: a=[%s]\n", a); // HelloWor
strncat(a, c, 10); // n > 長度,全連接
printf("strncat 10: a=[%s]\n", a); // HelloWor!!!
return 0;
}

所有 str* 函數均不支援源目重疊:
char buf[20] = "ABCDEF";
strcat(buf, buf + 2); // 未定義行為!src 在 dest 內部
需用 memmove 處理重疊區域。
| 函數 | 標準 | 特點 |
|---|---|---|
strlcat | BSD (非標準) | 接收 dest 總容量,保證不溢出,返回嘗試長度 |
strcat_s | C11 Annex K | 帶運行時約束檢查,違規調用處理函數 |
snprintf | C99 | snprintf(dest, size, "%s%s", dest, src) 通用安全 |
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix) 一次完成,自動截斷、保證結束符strncat(dest, src, sizeof(dest) - strlen(dest) - 1)-Wstringop-overflow (GCC/Clang) 捕獲潛在溢出strcat 是不安全的歷史遺留,strncat 提供長度限制但參數語義易混淆 (n 是追加字元數而非總容量)。現代 C 代碼應統一遷移至 snprintf 或 strlcat,從源頭消除緩衝區溢出隱患。