C语言常用字符串处理函数详解与手动实现

周叙
周叙
发布时间:2026-10-15 | 4人浏览过
原创

在C语言开发中,频繁涉及字符串拼接、拷贝、比较及内存清零等操作。掌握 string.h 库中的核心函数不仅是编程基础,更是理解指针与内存管理的关键。本文将逐一剖析 strlen、strcat、strcpy、strcmp 和 memset 的工作原理,并通过手写实现揭示其内部逻辑。

一、计算字符串长度:strlen()

strlen 用于获取字符串的有效字符数量,统计范围从起始地址开始直到遇到空字符 '\0' 为止,不包含终止符本身。

第1步:理解函数原型与返回值

原型为 size_t strlen(const char *str)。关键在于返回值类型为 size_t(无符号整数),这意味着结果永远大于等于零。若传入非标准字符串(末尾无 '\0'),函数将越界读取直到随机遇见空值,引发未定义行为。

第2步:手动实现逻辑分析

实现的核心在于利用指针遍历。由于输入是只读的(const),我们需要创建一个局部临时指针进行位移,同时累加计数器,直到发现终止符后返回计数值。以下为完整的测试代码:

#include <stdio.h>
#include <string.h>

// 手动实现 strlen
size_t my_strlen(const char *str)
{
    size_t len = 0;
    while (*str++ != '\0') {
        len++;
    }
    return len;
}

int main()
{
    const char *p = "hello world";
    // 建议打印使用 %zu 格式说明符对应 size_t 类型
    printf("len_1 = %zu, len_2 = %zu\n", my_strlen(p), strlen(p));
    return 0;
}

该操作的结果直观反映了字符串的字符密度,常用于分配动态内存时的尺寸估算。

二、连接字符串:strcat()

strcat 的作用是将源字符串(src)追加到目标字符串(dest)的末尾。目标字符串原本的 '\0' 会被覆盖,新字符串以 src 最后的 '\0' 结尾。

第1步:安全警告与参数

原型 char *strcat(char *dest, const char *src) 中,dest 必须是可修改的缓冲区且拥有足够剩余空间。这是导致缓冲区溢出攻击的高频点,务必确保 sizeof(dest) > strlen(dest) + strlen(src) + 1。

第2步:底层移位原理

实现步骤分为两阶段:首先移动 dest 指针至末尾的 '\0' 处;随后逐个将 src 的内容搬运并覆盖,最后手动补上结束符。

#include <stdio.h>
#include <string.h>

// 手动实现 strcat
char *my_strcat(char *dest, const char *src)
{
    // 寻找 dest 的尾部
    char *start = dest;
    while (*dest != '\0') {
        dest++;
    }
    // 执行复制与追加
    while (*src != '\0') {
        *dest++ = *src++;
    }
    *dest = '\0'; // 重要:必须添加结束标志
    return start;
}

int main()
{
    char p1[20] = "hello ";
    const char *q = "world";
    
    my_strcat(p1, q);
    printf("Result: %s\n", p1);
    return 0;
}

三、字符串拷贝:strcpy()

strcpy 将源字符串完整地复制到目标位置,包括尾部的 '\0'。如果两个区域重叠,其行为未定义。

第1步:易混淆的风险点

原型 char *strcpy(char *dest, const char *src)。与 strncpy 不同,它不限制拷贝长度,只要源串长于目标数组容量,就会破坏栈/堆结构。现代编程中推荐优先使用带长度控制的替代方案。

第2步:指针搬运过程

直接循环赋值是最简单的实现方式:*dest++ = *src++;。当源指针指向 '\0' 时停止,此时目标指针也已正确定位。

#include <stdio.h>
#include <string.h>

// 手动实现 strcpy
char *my_strcpy(char *dest, const char *src)
{
    char *result = dest;
    while ((*dest++ = *src++) != '\0'); 
    // 注意上述循环体为空,因为赋值表达式返回被赋的值
    return result;
}

int main()
{
    char buf[20] = {0};
    my_strcpy(buf, "Hello C");
    printf("Copied: %s\n", buf);
    return 0;
}

四、字典序比较:strcmp()

strcmp 按字节顺序比较两个字符串,依据 ASCII 码差值决定大小关系。

第1步:返回值解读

int strcmp(const char *str1, const char *str2):

  • 返回负数:str1 小于 str2(字典序靠前);
  • 返回零:两者相等;
  • 返回正数:str1 大于 str2。

第2步:逐位相减的逻辑

实现时需同步推进两个指针,一旦遇到不同字符或任一端结束,立即返回它们的 ASCII 差值 (*str1 - *str2)。若一直相等直到结束符,则返回0。

#include <stdio.h>
#include <string.h>

int my_strcmp(const char *s1, const char *s2)
{
    while (*s1 && (*s1 == *s2)) {
        s1++;
        s2++;
    }
    return *s1 - *s2;
}

int main()
{
    // 比较 'a' 和 'b' 的差异通常为正,反之负
    int res = my_strcmp("apple", "apply");
    printf("Compare result: %d\n", res);
    return 0;
}

五、内存填充:memset()

memset 是最高效的内存初始化手段,它将一块连续内存区域的所有字节设置为指定值。

第1步:应用场景与陷阱

原型 void *memset(void *ptr, int value, size_t num)。最常用于将数组清零(设为0)或赋值为 -1(二进制全1)。切勿将非整型结构体设为非零值,会导致对象状态非法。

第2步:字节级操作实现

虽然 value 参数是 int,但实际填充的是其低8位(即 unsigned char)的值。实现时必须先将目标转为字节指针进行操作。

#include <stdio.h>
#include <string.h>
#include <stddef.h>

void *my_memset(void *ptr, int val, size_t count)
{
    if (!ptr) return NULL;
    unsigned char *p = (unsigned char *)ptr;
    while (count--) {
        *p++ = (unsigned char)val;
    }
    return ptr;
}

int main()
{
    char arr[] = "Original Text";
    my_memset(arr, '*', 4);
    printf("Modified: %s\n", arr); // 输出 ****nal Text
    return 0;
}

以上就是对C语言常用字符串与内存处理函数的详细解析与源码实践,更多关于C语言进阶技巧的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表