字符串比较
基本比较
#include <stdio.h> #include <string.h> int main() { // 基本比较示例 const char *str1 = "apple"; const char *str2 = "banana"; const char *str3 = "apple"; const char *str4 = "Apple"; // 注意:首字母大写 printf("基础比较演示:\n"); printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str2, strcmp(str1, str2)); printf("strcmp(\"%s\", \"%s\") = %d\n", str2, str1, strcmp(str2, str1)); printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str3, strcmp(str1, str3)); printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str4, strcmp(str1, str4)); return 0; }输出结果分析:
strcmp("apple", "banana") = -1 (或负数) strcmp("banana", "apple") = 1 (或正数) strcmp("apple", "apple") = 0 strcmp("apple", "Apple") = 32 (或正数,'a' - 'A' = 97 - 65 = 32)3.1.2 strcmp的返回值真相
strcmp的实际返回值是实现定义的,但标准保证:
如果
s1 < s2,返回负数如果
s1 == s2,返回0如果
s1 > s2,返回正数
具体的数值取决于编译器:
#include <stdio.h> #include <string.h> void demonstrate_return_values() { printf("=== strcmp返回值实验 ===\n\n"); // 不同的编译器可能产生不同的具体数值 const char *s1 = "a"; // ASCII 97 const char *s2 = "b"; // ASCII 98 const char *s3 = "A"; // ASCII 65 int result1 = strcmp(s1, s2); int result2 = strcmp(s1, s3); printf("strcmp(\"%s\", \"%s\") = %d\n", s1, s2, result1); printf("ASCII差值: 'a'(%d) - 'b'(%d) = %d\n\n", 'a', 'b', 'a' - 'b'); printf("strcmp(\"%s\", \"%s\") = %d\n", s1, s3, result2); printf("ASCII差值: 'a'(%d) - 'A'(%d) = %d\n\n", 'a', 'A', 'a' - 'A'); // 常见误区:认为返回的是-1, 0, 1 printf("重要提醒:\n"); printf("strcmp不一定返回-1, 0, 1!\n"); printf("它只保证负/零/正。\n\n"); printf("if (strcmp(s1, s2) < 0) // 总是正确\n"); printf("if (strcmp(s1, s2) == 0) // 总是正确\n"); printf("if (strcmp(s1, s2) > 0) // 总是正确\n"); }它只保证负/零/正
strcmp比较不同版本
版本1:
int simple_strcmp(const char *s1, const char *s2) { // 最基础的实现 while (*s1 && (*s1 == *s2)) { s1++; s2++; } return *(unsigned char*)s1 - *(unsigned char*)s2; }版本2:
int detailed_strcmp(const char *s1, const char *s2) { // 步骤1:输入验证(标准strcmp不验证,但我们应该考虑) if (s1 == NULL || s2 == NULL) { // 标准行为:传入NULL是未定义的,可能导致崩溃 // 我们这里做安全处理 if (s1 == s2) return 0; // 两个都是NULL if (s1 == NULL) return -1; // NULL被认为小于任何字符串 if (s2 == NULL) return 1; // 任何字符串大于NULL } // 步骤2:逐字符比较 // 使用unsigned char*非常重要! // 原因:C语言的char可能是有符号的 // 比如,char值128会被当作-128处理 const unsigned char *p1 = (const unsigned char *)s1; const unsigned char *p2 = (const unsigned char *)s2; // 步骤3:循环直到发现不同或到达字符串结尾 while (*p1 && *p1 == *p2) { p1++; p2++; } // 步骤4:返回差值 // 当两个字符串相同时,*p1和*p2都是'\0',返回0 // 当发现不同时,返回两个字符的ASCII码差值 return *p1 - *p2; }常见使用情况
1:排序比较
#include <stdio.h> #include <string.h> #include <stdlib.h> // 用于qsort的比较函数 int compare_strings(const void *a, const void *b) { const char **str1 = (const char **)a; const char **str2 = (const char **)b; return strcmp(*str1, *str2); } void sort_strings_demo() { const char *fruits[] = { "banana", "apple", "cherry", "date", "elderberry" }; int count = sizeof(fruits) / sizeof(fruits[0]); printf("排序前:\n"); for (int i = 0; i < count; i++) { printf("%d: %s\n", i, fruits[i]); } qsort(fruits, count, sizeof(char*), compare_strings); printf("\n排序后:\n"); for (int i = 0; i < count; i++) { printf("%d: %s\n", i, fruits[i]); } }2:switch语句的替代
// strcmp不能直接用于switch,但可以这样处理 int handle_command(const char *cmd) { if (strcmp(cmd, "start") == 0) { return 1; } else if (strcmp(cmd, "stop") == 0) { return 2; } else if (strcmp(cmd, "pause") == 0) { return 3; } return 0;3:常见错误
void common_mistakes() { // 错误1:误用返回值 char *s1 = "hello"; char *s2 = "world"; printf("误用返回值\n"); printf("if (strcmp(s1, s2)) { ... }\n"); printf("// 这实际检查的是:如果s1 != s2\n\n"); printf("明确检查\n"); printf("if (strcmp(s1, s2) == 0) { // 相等 }\n"); printf("if (strcmp(s1, s2) != 0) { // 不相等 }\n"); printf("if (strcmp(s1, s2) < 0) { // s1 < s2 }\n"); printf("if (strcmp(s1, s2) > 0) { // s1 > s2 }\n\n"); // 错误2:传递NULL指针 printf("传递NULL指针\n"); printf("strcmp(NULL, \"test\"); // 未定义行为,可能崩溃\n\n"); // 错误3:比较未初始化的字符串 char buffer[10]; printf("比较未初始化的字符串\n"); printf("char buffer[10];\n"); printf("strcmp(buffer, \"test\"); // 未定义行为\n\n"); }strcmp的变体函数
strncmp:带长度限制的比较
void demonstrate_strncmp() { printf("=== strncmp演示 ===\n\n"); const char *s1 = "hello world"; const char *s2 = "hello there"; // 比较前5个字符 int result1 = strncmp(s1, s2, 5); printf("strncmp(\"%s\", \"%s\", 5) = %d\n", s1, s2, result1); printf("前5个字符相同:\"hello\"\n\n"); // 比较前6个字符 int result2 = strncmp(s1, s2, 6); printf("strncmp(\"%s\", \"%s\", 6) = %d\n", s1, s2, result2); printf("第6个字符不同:'w' vs 't'\n\n"); // 实际应用:比较固定前缀 const char *url = "https://example.com"; if (strncmp(url, "https://", 8) == 0) { printf("URL使用HTTPS协议\n"); } // 安全优势:避免缓冲区溢出 char user_input[10]; // 假设用户输入可能很长... const char *expected = "password"; // 安全比较,只比较前9个字符(包括'\0') if (strncmp(user_input, expected, sizeof(user_input)) == 0) { printf("密码匹配\n"); } }strcasecmp:不区分大小写的比较
#include <strings.h> // 注意:不是string.h! #include <ctype.h> // 自定义实现(如果系统没有提供) int custom_strcasecmp(const char *s1, const char *s2) { if (s1 == s2) return 0; if (s1 == NULL) return -1; if (s2 == NULL) return 1; while (*s1 && *s2) { char c1 = tolower((unsigned char)*s1); char c2 = tolower((unsigned char)*s2); if (c1 != c2) { return c1 - c2; } s1++; s2++; } // 至少有一个字符串到达结尾 return tolower((unsigned char)*s1) - tolower((unsigned char)*s2); } void demonstrate_case_insensitive() { printf("=== 大小写不敏感比较 ===\n\n"); const char *names[] = { "alice", "Alice", "ALICE", "aLiCe", "bob", "Bob", "BOB" }; printf("区分大小写比较(strcmp):\n"); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { printf("strcmp(\"%s\", \"%s\") = %d\n", names[i], names[j], strcmp(names[i], names[j])); } } printf("\n不区分大小写比较:\n"); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { printf("strcasecmp(\"%s\", \"%s\") = %d\n", names[i], names[j], strcasecmp(names[i], names[j])); } } }memcmp:内存比较(可处理含'\0'的数据)
void demonstrate_memcmp() { printf("=== memcmp vs strcmp ===\n\n"); // 情况1:二进制数据包含'\0' char data1[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o', 'r', 'l', 'd' }; char data2[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'X', 'y', 'z' }; printf("数据包含'\\0'的情况:\n"); printf("strcmp会在第一个'\\0'处停止\n"); printf("memcmp会继续比较指定长度的所有字节\n\n"); // 比较前6个字节 printf("memcmp(data1, data2, 6) = %d\n", memcmp(data1, data2, 6)); printf("strcmp(data1, data2) = %d\n", strcmp(data1, data2)); // 比较前11个字节 printf("memcmp(data1, data2, 11) = %d\n", memcmp(data1, data2, 11)); // 实际应用:比较结构体 typedef struct { int id; char name[20]; float score; } Student; Student s1 = {1, "Alice", 95.5}; Student s2 = {1, "Alice", 95.5}; printf("\n结构体比较:\n"); printf("memcmp(&s1, &s2, sizeof(Student)) = %d\n", memcmp(&s1, &s2, sizeof(Student))); // 警告:memcmp可能因为内存对齐填充而失败 }