strcmp函数如何实现字符串比较?

99ANYc3cd6
预计阅读时长 21 分钟
位置: 首页 C语言 正文

strcmp 是 C 标准库 <string.h> 中的一个函数,用于比较两个字符串。

c语言strcmp函数实现
(图片来源网络,侵删)

strcmp 函数的功能和原型

strcmp 函数比较两个以 null 结尾的字符串 s1s2

函数原型:

int strcmp(const char *s1, const char *s2);

返回值:

  • 返回 0: s1s2 完全相同。
  • 返回一个负整数: s1 小于 s2。(通常是 s1 中第一个不匹配字符的 ASCII 码减去 s2 中对应字符的 ASCII 码)
  • 返回一个正整数: s1 大于 s2。(通常是 s1 中第一个不匹配字符的 ASCII 码减去 s2 中对应字符的 ASCII 码)

比较规则: strcmp 会从左到右逐个字符比较两个字符串的 ASCII 码值,比较在以下两种情况之一发生时停止:

c语言strcmp函数实现
(图片来源网络,侵删)
  1. 遇到第一个不相等的字符。
  2. 两个字符串都同时到达了结尾(即 \0 字符)。

示例:

  • strcmp("apple", "apple") 返回 0
  • strcmp("apple", "banana") 返回一个负数(因为 'a' < 'b')。
  • strcmp("banana", "apple") 返回一个正数(因为 'b' > 'a')。
  • strcmp("apple", "apples") 返回一个负数(因为第一个字符串先结束,相当于在比较 \0 和 's',而 \0 的 ASCII 码是 0)。

手动实现 strcmp

理解了功能后,我们可以自己动手实现一个 strcmp 函数,核心思想就是循环比较字符,直到找到差异或字符串结束。

最基础的实现

这是最直观、最容易理解的实现方式。

/**
 * @brief 自定义 strcmp 函数实现
 * @param s1 第一个字符串
 * @param s2 第二个字符串
 * @return  s1 == s2, 返回 0;
 *          s1 < s2, 返回一个负数;
 *          s1 > s2, 返回一个正数.
 */
int my_strcmp(const char *s1, const char *s2) {
    // 循环,直到 s1 或 s2 指向 '\0',或者两个字符不相等
    while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
        s1++; // 移动到 s1 的下一个字符
        s2++; // 移动到 s2 的下一个字符
    }
    // 循环结束后,*s1 和 *s2 要么是 '\0',要么是不相等的字符
    // 将这两个字符相减,结果就是 strcmp 规定的返回值
    // s1 是 "abc" s2 是 "abd",*s1 是 'c', *s2 是 'd','c' - 'd' 是一个负数
    // s1 是 "abc" s2 是 "ab",*s1 是 'c', *s2 是 '\0','c' - '\0' 是一个正数
    // s1 是 "ab" s2 是 "abc",*s1 是 '\0', *s2 是 'c','\0' - 'c' 是一个负数
    return *s1 - *s2;
}

代码解析:

c语言strcmp函数实现
(图片来源网络,侵删)
  1. while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2): 这是循环条件。
    • *s1 != '\0' && *s2 != '\0': 确保两个字符串都还没有结束。
    • *s1 == *s2: 确保当前字符是相等的。
    • 只有当这三个条件都满足时,循环才会继续,意味着字符串仍然“相同”。
  2. s1++; s2++;: 如果当前字符相同,就将指针移动到下一个字符进行比较。
  3. return *s1 - *s2;: 当循环退出时,说明要么有一个字符串结束了,要么找到了不匹配的字符,将两个指针当前所指向的字符相减,其差值就完全符合 strcmp 的返回值要求。

更紧凑的实现

我们可以将版本一的逻辑进行简化,利用 C 语言中布尔表达式的特性。

int my_strcmp_compact(const char *s1, const char *s2) {
    // 循环直到找到不匹配的字符或任一字符串结束
    while (*s1 == *s2) {
        // s1 指向了 '\0',s2 也必然指向 '\0'(因为 *s1 == *s2)
        // 此时循环会结束,*s1 和 *s2 都是 '\0'
        if (*s1 == '\0') {
            return 0; // 字符串完全相同
        }
        s1++;
        s2++;
    }
    // 循环结束,说明 *s1 不等于 *s2
    return *(unsigned char *)s1 - *(unsigned char *)s2;
}

或者更进一步,直接利用循环结束后的状态:

int my_strcmp_compact_2(const char *s1, const char *s2) {
    while (*s1 && *s1 == *s2) { // *s1 会自动判断是否为 '\0'
        s1++;
        s2++;
    }
    // 当循环退出时,要么 *s1 是 '\0',要么 *s1 != *s2
    // 将其差值返回即可
    return *(const unsigned char *)s1 - *(const unsigned char *)s2;
}

这个版本更简洁,但版本一可能对初学者来说逻辑更清晰。


一个重要的细节:unsigned char

在标准的 strcmp 实现中,字符通常被转换为 unsigned char 类型后再进行比较,这主要是为了处理字符的符号问题。

在 C 语言中,char 类型可能是 signed char(范围 -128 到 127)或 unsigned char(范围 0 到 255),这取决于编译器的实现。charsigned 的,ASCII 码大于 127 的字符(或某些非 ASCII 字符)会被解释为负数。

如果我们直接比较两个 char,其中一个可能是负数,另一个是正数,可能会导致意外的结果,比较 '\xff''\x01'

  • charsigned 的,'\xff' 会被解释为 -1。
  • '\x01' 是 1。
  • -1 < 1 的结果是 true
  • 但如果我们把它们当作无符号数,255 > 1

标准库为了保证比较是基于字符的原始数值(0-255),通常会进行类型转换。

一个更健壮的实现是:

/**
 * @brief 更健壮的 strcmp 实现,考虑了 char 的符号问题
 */
int my_strcmp_robust(const char *s1, const char *s2) {
    while (1) {
        unsigned char c1 = *s1;
        unsigned char c2 = *s2;
        if (c1 != c2) {
            return c1 - c2;
        }
        if (c1 == '\0') { // c2 也必然是 '\0'
            return 0;
        }
        s1++;
        s2++;
    }
}

这个版本明确地将字符转换为 unsigned char,确保了比较行为的一致性,不受平台 char 类型默认符号的影响。


完整示例代码

下面是一个完整的 C 程序,演示了如何使用我们自己实现的 my_strcmp 函数,并与标准库的 strcmp 进行对比。

#include <stdio.h>
// 版本一:基础实现
int my_strcmp(const char *s1, const char *s2) {
    while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
        s1++;
        s2++;
    }
    return *s1 - *s2;
}
int main() {
    const char *str1 = "hello";
    const char *str2 = "hello";
    const char *str3 = "world";
    const char *str4 = "hell";
    const char *str5 = "hellO";
    printf("比较 \"%s\" 和 \"%s\":\n", str1, str2);
    printf("标准库 strcmp:   %d\n", strcmp(str1, str2));
    printf("自定义 my_strcmp: %d\n\n", my_strcmp(str1, str2));
    printf("比较 \"%s\" 和 \"%s\":\n", str1, str3);
    printf("标准库 strcmp:   %d\n", strcmp(str1, str3));
    printf("自定义 my_strcmp: %d\n\n", my_strcmp(str1, str3));
    printf("比较 \"%s\" 和 \"%s\":\n", str1, str4);
    printf("标准库 strcmp:   %d\n", strcmp(str1, str4));
    printf("自定义 my_strcmp: %d\n\n", my_strcmp(str1, str4));
    printf("比较 \"%s\" 和 \"%s\":\n", str1, str5);
    printf("标准库 strcmp:   %d\n", strcmp(str1, str5));
    printf("自定义 my_strcmp: %d\n\n", my_strcmp(str1, str5));
    return 0;
}

可能的输出:

比较 "hello" 和 "hello":
标准库 strcmp:   0
自定义 my_strcmp: 0
比较 "hello" 和 "world":
标准库 strcmp:   -15
自定义 my_strcmp: -15
比较 "hello" 和 "hell":
标准库 strcmp:   111
自定义 my_strcmp: 111
比较 "hello" 和 "hellO":
标准库 strcmp:   -79
自定义 my_strcmp: -79

(注:'o' - 'O' 的具体值取决于你的系统字符集,但结果会是同一个负数)

实现 strcmp 的核心步骤是:

  1. 使用一个 while 循环来遍历两个字符串。
  2. 在循环条件中,判断当前字符是否相等,并且两个字符串是否都未结束。
  3. 当循环结束时,第一个不相等字符的差值就是最终结果。
  4. 为了更好的可移植性,最好将字符转换为 unsigned char 类型后再进行比较。

希望这个详细的解释能帮助你完全理解 strcmp 函数的原理和实现!

-- 展开阅读全文 --
头像
织梦责任编辑修改入口在哪?
« 上一篇 今天
dede免费酒类模板哪里找?好用吗?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]