手撕字符串比较函数
2026/8/22大约 2 分钟
手撕 strcmp 与 strncmp
strcmp 和 strncmp 的核心完全一样:
逐字符比较,找到第一个不同的字符就结束。
区别只有一个:
strcmp
→ 一直比较到 '\0'
strncmp
→ 最多比较 count 个字符1. 手撕 strcmp
int my_strcmp(const char *str1, const char *str2)
{
assert(str1 && str2);
while (*str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
}
return *(unsigned char *)str1
- *(unsigned char *)str2;
}例如:
str1:a b c d
str2:a b c e
↓ ↓ ↓
相同继续
↓
d != e
↓
返回 d - e返回值只看:
< 0 → str1 < str2
= 0 → str1 == str2
> 0 → str1 > str2为什么转 unsigned char?
char 是否带符号由实现决定。
计算机内部存储有符号整数用的是补码(Two's complement)。对于 8 位二进制 1111 1111(即 0xFF):
- 最高位(第 7 位)是 1,表示这是一个负数。
- 计算其绝对值的方法:“取反加一”。
- 取反:
1111 1111->0000 0000 - 加一:
0000 0000 + 1 = 0000 0001(即十进制的 1)
- 取反:
- 所以,它是
-1
假设:
str1 当前字符 = 0xFF
str2 当前字符 = 0x01
如果直接:
*str1 - *str2
并且 char 是 signed:
0xFF → -1
0x01 → 1
-1 - 1 = -2
于是得到:
str1 < str2
但如果按照 unsigned char:
0xFF → 255
0x01 → 1
255 - 1 = 254
这时候:
str1 > str2
这两个结果的符号完全相反。
所以如果不转换,就可能改变:
strcmp(str1, str2)
应该返回:
< 0
= 0
> 0
中的哪一种。2. 手撕 strncmp
int my_strncmp(const char *str1, const char *str2, size_t count)
{
assert(str1 && str2);
while (count && *str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
count--;
}
if (count == 0)
return 0;
return *(unsigned char *)str1
- *(unsigned char *)str2;
}核心就是在 strcmp 上增加:
count控制最多比较多少个字符。
例如:
my_strncmp("abc", "abd", 2);只比较:
a == a
b == b比较次数达到 2 后,直接:
return 0不会继续比较 c 和 d。
3. 最容易错的地方
不能只写:
while (... && count)然后循环结束后直接:
return *str1 - *str2;因为:
my_strncmp("abc", "abd", 2);比较完两个字符后:
count = 0
str1 → c
str2 → d这时候再比较 c 和 d 就越界了 strncmp 的比较范围。
所以必须:
if (count == 0)
return 0;4. 两个函数对比
strcmp
↓
逐字符比较
↓
不同 → 返回
'\0' → 返回
strncmp
↓
逐字符比较
↓
不同 → 返回
'\0' → 返回
count == 0 → 返回 0魂
strcmp:比较到结束。strncmp:比较到结束,但最多count个。
找到第一个不同字符后,按unsigned char比较。

