欢迎光临
我们一直在努力

C语言学习笔记20260618-字符串库函数模拟实现

C语言学习笔记20260618-字符串库函数模拟实现

一、学习目标

深入理解C语言中四个核心字符串库函数(strlen、strcpy、strcat、strcmp)的底层实现原理,掌握计数器、递归、指针运算等多种编程技巧,并学习如何通过断言(assert)和const修饰符来编写健壮、安全的底层代码。

二、模拟实现 strlen(获取字符串长度)

2.1 核心思路与三种实现方式

strlen 用于计算字符串中有效字符的个数(不包括末尾的 ‘\\0’)。代码展示了三种经典的实现思路:

  • 计数器方式:定义一个临时变量 count,遍历字符串直到遇到 ‘\\0’,每遍历一个字符 count++。这是最直观、最容易理解的暴力解法。
  • 递归方式:利用函数栈的特性。如果当前字符是 ‘\\0’,返回0;否则返回 1 + 下一个字符的长度。这种方式代码极其精简,但深层递归可能导致栈溢出。
  • 指针-指针方式:记录字符串的起始地址 s,用一个指针 p 遍历到末尾。利用C语言指针运算的特性,p – s 直接得出两个地址之间相隔的字符数。这是效率最高且最具C语言特色的写法。
  • 2.2 代码实现与规范

    // 方式1:计数器方式
    int my_strlen(const char* str)
    {
    assert(str != NULL); // 防止传入空指针
    int count = 0;
    while (*str)
    {
    count++;
    str++;
    }
    return count;
    }

    // 方式2:不能创建临时变量计数器(递归)
    int my_strlen(const char* str)
    {
    assert(str != NULL);
    if (*str == '\\0')
    return 0;
    else
    return 1 + my_strlen(str + 1);
    }

    // 方式3:指针-指针的方式
    int my_strlen(char* s)
    {
    assert(s != NULL);
    char* p = s;
    while (*p != '\\0')
    p++;
    return p – s;
    }

    2.3 代码优化细节

    • const 修饰符:函数参数应使用 const char* str。因为 strlen 只是读取字符串,不需要修改它。加上 const 既能防止函数内部误修改数据,又能体现良好的编程规范。
    • 返回值类型:标准库中 strlen 的返回值类型是 size_t(无符号整型),模拟实现时建议保持一致,以避免有符号与无符号数比较时产生的潜在警告。

    三、模拟实现 strcpy(字符串拷贝)

    3.1 核心思路

    strcpy 将源字符串(包括 ‘\\0’)完整拷贝到目标空间。最精妙的实现是将赋值操作直接放在 while 循环的判断条件中:while ((*dest++ = *src++))。

    3.2 代码实现

    char* my_strcpy(char* dest, const char* src)
    {
    assert(dest != NULL);
    assert(src != NULL);

    char* ret = dest; // 保存目标字符串的起始地址,支持链式调用
    while ((*dest++ = *src++))
    {
    ; // 循环体为空,逻辑全在判断条件中
    }
    return ret;
    }

    3.3 代码解析

    • 后置++的妙用:*dest++ = *src++ 利用了后置++“先使用,后自增”的特性。它先将 src 指向的字符赋值给 dest,然后两个指针同时向后移动一位。
    • 自动拷贝 ‘\\0’:当 src 遍历到末尾的 ‘\\0’ 时,‘\\0’ 会被赋值给 dest。在C语言中,‘\\0’ 的 ASCII 码为 0,即逻辑“假”,此时 while 循环自然终止。这行代码完美地解决了“拷贝字符”和“终止循环”两个任务。
    • const 保护源串:源字符串 src 只读,必须用 const 修饰,防止在拷贝过程中被意外篡改。

    四、模拟实现 strcat(字符串追加)

    4.1 核心思路

    strcat 将源字符串追加到目标字符串的末尾。实现逻辑分为两步:首先找到目标字符串的结束符 ‘\\0’,然后从该位置开始执行与 strcpy 相同的拷贝逻辑。

    4.2 代码实现

    char* my_strcat(char* dest, const char* src)
    {
    assert(dest != NULL);
    assert(src != NULL);

    char* ret = dest; // 保存目标字符串的起始地址

    // 第一步:找到目标字符串的末尾('\\0'的位置)
    while (*dest)
    {
    dest++;
    }

    // 第二步:从末尾开始拷贝源字符串(同strcpy逻辑)
    while ((*dest++ = *src++))
    {
    ;
    }

    return ret;
    }

    4.3 关键细节与隐患

    • 重叠内存隐患:标准 strcat 不支持目标字符串和源字符串是同一块内存(即自己追加自己)。因为当第一个 while 找到 ‘\\0’ 后,第二个 while 的拷贝操作会立刻将这个 ‘\\0’ 覆盖掉,导致后续永远找不到终止符,引发死循环或缓冲区溢出。在实际工程中,应使用更安全的 strncat 或 snprintf。

    五、模拟实现 strcmp(字符串比较)

    5.1 核心思路

    strcmp 逐字符比较两个字符串对应位置字符的 ASCII 码值。标准规定:str1 > str2 返回大于0的数,相等返回0,小于返回小于0的数。

    5.2 代码实现

    int my_strcmp(const char* src, const char* dst)
    {
    assert(src != NULL);
    assert(dst != NULL);

    int ret = 0;
    // 逐字符比较,直到出现不同字符或遇到字符串末尾
    while (!(ret = *(unsigned char*)src – *(unsigned char*)dst) && *dst)
    {
    ++src;
    ++dst;
    }

    // 将返回值归一化为 -1, 0, 1
    if (ret < 0)
    ret = –1;
    else if (ret > 0)
    ret = 1;
    return ret;
    }

    5.3 代码精妙之处

    • 无符号数相减:代码中使用了 *(unsigned char*)src – *(unsigned char*)dst。将 char* 强转为 unsigned char* 是为了防止 char 在某些编译器下是有符号数(signed char),当遇到 ASCII 码大于 127 的扩展字符时,有符号减法可能导致错误的比较结果。
    • 循环终止条件:!(ret = … ) && *dst。只要两个字符相减结果为0(即字符相同)且未遇到字符串末尾,就继续比较下一个字符。
    • 返回值归一化:标准库只要求返回正数、0或负数。代码最后通过 if-else 将结果归一化为 -1、0、1,这使得返回值更加规范,便于调用者进行精确的逻辑判断。
    赞(0)
    未经允许不得转载:171主机测评 » C语言学习笔记20260618-字符串库函数模拟实现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址