核心字符串函數分類
1. 比較函數
int strcmp(const char *s1, const char *s2); // 全比較,區分大小寫
int strncmp(const char *s1, const char *s2, size_t n); // 前 n 字符
int strcasecmp(const char *s1, const char *s2); // 忽略大小寫(POSIX)
- 返回值:<0 (s10 (s1>s2),值為首個不同字符 ASCII 差
- 常用模式:
if (!strcmp(s1, s2)) 判斷相等
2. 查找函數
char *strchr(const char *s, int c); // 查字符,返回首次出現地址
char *strrchr(const char *s, int c); // 最後一次出現
char *strstr(const char *haystack, const char *needle); // 查子串
char *strcasestr(const char *h, const char *n); // 忽略大小寫查子串
3. 複製函數
char *strcpy(char *restrict dst, const char *restrict src); // 不安全
char *strncpy(char *dst, const char *src, size_t n); // 指定長度
char *strdup(const char *s); // 自動 malloc 複製,需 free 釋放
strcpy 不檢查目標空間,**極易導致緩衝區溢出**
strncpy 若 src 長度 ≥ n,**不會自動添加結束符 '\0'**,需手動補齊
- 推薦:
snprintf(dst, size, "%s", src) 或 strlcpy(BSD)
4. 連接函數
char *strcat(char *restrict dst, const char *restrict src); // 不安全
char *strncat(char *dst, const char *src, size_t n); // 追加至多 n 字符
- 同樣需確保目標空間足夠
- 性能陷阱:
strcat 每次需掃描 dst 尾部,循環連接呈 O(N²)
5. 長度與其他
size_t strlen(const char *s); // 不含 '\0'
char *strtok(char *s, const char *delim); // 分割,破壞原串,非線程安全
char *strtok_r(char *s, const char *delim, char **saveptr); // 線程安全版
void *memcpy(void *dst, const void *src, size_t n); // 內存複製,不關心 '\0'
void *memmove(void *dst, const void *src, size_t n); // 重疊安全
int memcmp(const void *s1, const void *s2, size_t n); // 內存比較
手寫實現精華(教學用)
strcmp
int my_strcmp(const char *s1, const char *s2) {
while (*s1 && *s1 == *s2) { s1++; s2++; }
return *(const unsigned char *)s1 - *(const unsigned char *)s2;
}
strcpy
char *my_strcpy(char *dst, const char *src) {
char *ret = dst;
while ((*dst++ = *src++) != '\0');
return ret;
}
strstr(暴力匹配)
char *my_strstr(const char *haystack, const char *needle) {
size_t n = strlen(needle);
if (n == 0) return (char *)haystack;
for (; *haystack; haystack++)
if (*haystack == *needle && !strncmp(haystack, needle, n))
return (char *)haystack;
return NULL;
}
安全編程準則
- **禁用**
gets、strcpy、strcat、sprintf
- 使用帶長度限制版本:
fgets、strncpy、strncat、snprintf
- 目標緩衝區大小傳遞給函數,內部嚴格檢查邊界
- 字符串操作前驗證指針非 NULL
- 涉及網絡/文件序列化時,顯式處理 '\0' 與長度前綴
現代替代方案
- C11 附錄 K:
strcpy_s、strcat_s、snprintf_s(需 __STDC_WANT_LIB_EXT1__)
- 開源安全庫:
SafeStr、Bstring、glib 字符串模組
- C++ 直接用
std::string、std::string_view