C 語言無內置字符串類型,字符串本質是 char* 指向的內存區域,以 '\0'(NUL,ASCII 0)標記結束。所有標準庫字符串函數均依賴此約定,**缺失 NUL 將導致未定義行為(緩衝區溢出、無限循環)**。
strlen:長度計算(不含 NUL)size_t strlen(const char *s);
size_t len = strlen(s);size_t(無符號),比較時注意類型轉換strcmp/strncmp:字典序比較int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n);
if (strcmp(a, b) == 0) 或 if (!strcmp(a, b))strncmp 限制比較前 n 字符,適合前綴匹配strcat/strncat:拼接(極易緩衝區溢出)char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n);
strncat 追加 src 前 n 字符,保證結果以 NUL 結尾snprintf(dest, size, "%s%s", dest, src) 或手動檢查剩餘空間dest,支持鏈式調用strchr/strrchr:字符查找char *strchr(const char *s, int c); // 首次出現 char *strrchr(const char *s, int c); // 最後出現
c 為 int 但按 unsigned char 解釋NULLfor (p=strchr(s,c); p; p=strchr(p+1,c))strcpy/strncpy:複製(經典陷阱)char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n);
strcpy:複製直到 NUL,**不檢查邊界**strncpy:複製前 n 字符,**若 src 短於 n,補 NUL 到 n;若 src ≥ n,結果不以 NUL 結尾**strncpy(dest, src, n-1); dest[n-1] = '\0';snprintf(dest, n, "%s", src) 保證 NUL 結尾strspn/strcspn:字符集跨度size_t strspn(const char *s, const char *accept); // 首個不在 accept 中的下標 size_t strcspn(const char *s, const char *reject); // 首個在 reject 中的下標
s += strspn(s, " \t\r\n");strstr:子串查找char *strstr(const char *haystack, const char *needle);
memmem (GNU 擴展)strtok:分詞器(有狀態、破壞原串、非線程安全)char *strtok(char *str, const char *delim);
str,後續傳 NULL 繼續分割char *token;
if ((token = strtok(str, " ,.!?")) != NULL) {
do {
printf("Token: %s\n", token);
} while ((token = strtok(NULL, " ,.!?")) != NULL);
}
strtok_r (POSIX) / strtok_s (C11 Annex K)| 陷阱 | 後果 | 對策 |
|---|---|---|
| 目標緩衝區過小 | 緩衝區溢出、安全漏洞 | 使用 snprintf、strlcpy/strlcat (BSD)、檢查長度 |
| 字符串無 NUL 結尾 | 讀越界、無限循環 | 初始化置零、strncpy 後手動加 NUL、靜態分析工具 |
strncpy 不補 NUL | 後續函數誤讀垃圾數據 | dest[n-1] = '\0' 或改用 snprintf |
strtok 破壞原串 | 原數據不可用、多線程衝突 | 傳入副本 strdup(s),或用 strtok_r |
| 字符串字面量修改 | 段錯誤(只讀段) | char s[] = "lit" 而非 char *s = "lit" |
| 有符號/無符號比較 | 邏輯錯誤(如 strlen(s) > -1 恆真) | 統一用 size_t,編譯器警告 -Wsign-compare |
strcpy_s、strcat_s、strtok_s 等,需定義 __STDC_WANT_LIB_EXT1__strlcpy/strlcat:保證 NUL 結尾,返回源長度,廣泛可用sds (Redis)、bstring、CString (Glib) —— 管理長度、容量、自動擴容std::string:混合編程時優先選擇__builtin_strlen 等,常優化為 SIMD 指令len = strlen(s); 緩存,而非循環中反覆調用mmap + memchr/memmem 避免拷貝字符串操作是 C 語言安全漏洞的高發區。內化「NUL 終結、長度檢查、副本隔離」三大原則,善用現代安全 API,才能在享受 C 效率的同時守住底線。