C 語言無內建字符串類型,字符串以 **空字符 `\0` 結尾的 `char` 陣列** 表示。比較兩字符串是否相等,**不可用 `==` 比較指針地址**,必須使用 `
| 函數 | 簽名 | 行為 | 返回值 | 適用場景 |
|---|---|---|---|---|
| `strcmp` | `int strcmp(const char* s1, const char* s2)` | 逐字節比較,直到 `\0` 或差異 | 0=相等, <0=s1 | 標準精確比較 |
| `strncmp` | `int strncmp(const char* s1, const char* s2, size_t n)` | 最多比較 n 字節 | 同上 | 防緩衝區溢出、比較前綴 |
| `strcasecmp` (POSIX) | `int strcasecmp(const char* s1, const char* s2)` | 忽略大小寫比較 | 同上 | 用戶輸入、配置項、不區分大小寫 |
| `strncasecmp` (POSIX) | `int strncasecmp(const char* s1, const char* s2, size_t n)` | 忽略大小寫 + 長度限制 | 同上 | 安全 + 不敏感 |
#include
#include
#include // for toupper/tolower
int main() {
int age;
printf("請輸入用戶年齡: ");
scanf("%d", &age);
if (age >= 18) {
printf("成年用戶,可繼續瀏覽\n");
} else if (age >= 10) {
char input[10];
printf("青少年用戶,確認繼續? (Y/N): ");
scanf("%9s", input); // 限制寬度防溢出
// 方法 1: 標準 strcmp (區分大小寫)
if (strcmp(input, "Y") == 0 || strcmp(input, "y") == 0) {
printf("確認繼續\n");
} else {
printf("取消瀏覽\n");
}
// 方法 2: strcasecmp (POSIX,忽略大小寫,推薦)
// if (strcasecmp(input, "Y") == 0) { ... }
} else {
printf("兒童用戶,不適合瀏覽\n");
}
return 0;
}
strcmp("abc", "abc") == 0 // 相等
strcmp("abc", "abd") < 0 // 'c' (99) < 'd' (100)
strcmp("abd", "abc") > 0
strcmp("abc", "abcd") < 0 // '\0' (0) < 'd' (100)
strcmp("ABC", "abc") < 0 // 'A' (65) < 'a' (97) — 區分大小寫!
#include // 注意:非 string.h
if (strcasecmp(input, "YES") == 0) { /* ... */ }
#ifdef _WIN32
#include
#define strcasecmp _stricmp
#else
#include
#endif
// 統一使用 strcasecmp
int my_strcasecmp(const char* s1, const char* s2) {
while (*s1 && *s2) {
char c1 = toupper((unsigned char)*s1);
char c2 = toupper((unsigned char)*s2);
if (c1 != c2) return c1 - c2;
s1++; s2++;
}
return toupper((unsigned char)*s1) - toupper((unsigned char)*s2);
}
關鍵細節: `touper((unsigned char)c)` 避免負值 `char` 導致未定義行為。
若輸入來源不可信 (網絡、文件、用戶),**必須限制比較長度**:
// 只比較前 3 字符,防止異常長輸入導致越界讀取
if (strncmp(input, "CMD", 3) == 0) {
// 處理命令...
}
// 配合 strnlen 確保字符串合法結尾
if (strnlen(input, MAX_LEN) < MAX_LEN && strncmp(input, "EXIT", 4) == 0) {
// 安全比較
}
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| `if (str1 == str2)` | 比較指針地址,非字符串內容 | `strcmp(str1, str2) == 0` |
| `if (strcmp(str1, str2))` | 相等返回 0 (假),不等返回非 0 (真) — 邏輯反轉 | `if (strcmp(str1, str2) == 0)` 相等分支 |
| `scanf("%s", buf)` 無寬度限制 | 緩衝區溢出漏洞 | `scanf("%99s", buf)` (留 1 位給 `\0`) |
| 比較未以 `\0` 結尾的字符陣列 | 越界讀取,未定義行為 | 確保字符串正確結尾,或用 `strncmp` 限制長度 |
| `strcmp` 用於二進制數據比較 | 遇 `\0` 提前停止 | 用 `memcmp(buf1, buf2, len)` 比較二進制塊 |
| 假設 `strcasecmp` 標準 C 庫函數 | Windows 無此函數,移植失敗 | 宏映射 `_stricmp` 或手寫通用版 |
int cmp_str(const void* a, const void* b) {
const char* s1 = *(const char**)a;
const char* s2 = *(const char**)b;
return strcmp(s1, s2); // 字典序排序
}
int cmp_str_case(const void* a, const void* b) {
return strcasecmp(*(const char**)a, *(const char**)b); // 忽略大小寫排序
}
char* names[] = {"Bob", "alice", "Charlie", "ALICE"};
qsort(names, 4, sizeof(char*), cmp_str_case);
// 結果: alice, ALICE, Bob, Charlie
字符串比較是 C 語言基礎中**最易錯、漏洞最多**的環節之一。建立「指針≠字符串、相等==0、輸入必限長、大小寫按需」的肌肉記憶,能消除 90% 的字符串處理 Bug。