strcat() 是 <string.h> 中用於字串尾部追加的核心函數。雖然用法看似簡單,但其「目標緩衝區必須足夠大」「不檢查邊界」「源目不可重疊」等特性,使其成為緩衝區溢出漏洞的高發區。本文系統梳理原型、行為、返回值、標準用法與安全替代方案。
#include <string.h>
char *strcat(char *dest, const char *src);
| 項目 | 說明 |
|---|---|
| dest | 目標字串指標,必須指向一個**足夠大、可寫、以 '\0' 結尾**的字元陣列。原有結尾 '\0' 會被覆蓋。 |
| src | 源字串指標,內容將被追加到 dest 末尾,src 本身不變。 |
| 返回值 | 返回 dest(起始地址),便於鏈式調用,如 strcat(strcat(buf, "A"), "B")。 |
| 核心行為 | 1. 定位 dest 末尾 '\0' 2. 從 src 複製字元覆蓋該 '\0' 及後續位置 3. 複製 src 的 '\0' 作為新結尾 |
#include <stdio.h>
#include <string.h>
int main(void) {
char str[80] = {0}; // 足夠大的緩衝區,初始化為 0 確保首個 '\0'
strcpy(str, "here ");
strcat(str, "is ");
strcat(str, "the ");
strcat(str, "xiaoz ");
puts(str); // 輸出: here is the xiaoz
return 0;
}
char dest[10] = "hello"; // 只有 10 字節
strcat(dest, " world!"); // 需 12 字節(含 '\0')-> 溢出!破壞棧內存,導致崩潰或安全漏洞
防範: 永遠確保 strlen(dest) + strlen(src) + 1 <= sizeof(dest)。
char dest[20]; // 未初始化,棧上隨機值
strcat(dest, "test"); // 從隨機位置找 '\0',行為未定義
防範: 定義時初始化 char dest[20] = {0}; 或先 strcpy(dest, "");。
char buf[20] = "hello";
strcat(buf, buf + 2); // src 指向 buf 內部 -> 未定義行為
標準規定: dest 與 src 所指記憶體區域**不得重疊**。需重疊操作請用 memmove。
char *p = "hello";
strcat(p, " world"); // p 指向唯讀段 -> 段錯誤
防範: 目標必須是可寫陣列 char arr[] = "hello";。
strncat 與 strlcat| 函數 | 原型 | 特性 | 可用性 |
|---|---|---|---|
strncat | char *strncat(char *dest, const char *src, size_t n) | 最多追加 n 字元,**不保證**結果以 '\0' 結尾(若 src 長度 >= n) | C89 標準,廣泛可用 |
strlcat | size_t strlcat(char *dest, const char *src, size_t dstsize) | **保證**結果以 '\0' 結尾,返回嘗試創建的總長度,便於檢測截斷 | BSD 擴展,Linux 需 #define _GNU_SOURCE,Windows 無 |
strncat 安全用法模式char dest[20] = "hello";
size_t remaining = sizeof(dest) - strlen(dest) - 1; // 預留 '\0' 位置
if (remaining > 0) {
strncat(dest, " world!", remaining);
}
malloc(strlen(a)+strlen(b)+1))。snprintf 進行格式化拼接:snprintf(buf, sizeof(buf), "%s%s", s1, s2); 更安全、可讀、可控。strcat(如網絡數據、用戶輸入)。strcat、strcpy、sprintf、gets 等不安全函數。strcat() 雖是 C 語言標準庫基石,但其「不檢查邊界」的設計反映了早期計算資源受限的歷史背景。現代 C 開發應將其視為**受限使用**函數,優先選擇 snprintf、strlcat 或 C++ std::string,從源頭消除緩衝區溢出風險。