C 语言实战:手动实现字符串核心函数(指针版)

96 阅读8分钟

C 语言实战:手动实现字符串核心函数(指针版)

字符串操作是 C 语言的核心技能之一,标准库<string.h>中的strcpystrcmpstrlenstrcat是最常用的字符串函数。本文基于你提供的代码,拆解手动实现这 4 个函数的核心逻辑(指针遍历),指出代码中的关键问题,并提供修复后的可运行版本,帮你吃透字符串指针操作的本质。

一、代码整体功能总结

这份代码通过指针逐字符遍历(以'\0'为字符串结束标志),手动实现了 C 标准库中 4 个核心字符串函数:

  • Strcpy:复制字符串(对应标准库strcpy);
  • Strcmp:比较字符串(对应标准库strcmp);
  • Strlen:计算字符串长度(对应标准库strlen);
  • Strcat:拼接字符串(对应标准库strcat);同时编写了测试用例验证每个函数的功能,核心思想是利用指针操作替代数组下标,更贴近字符串的底层存储逻辑。

二、逐函数解析(实现逻辑 + 问题 + 优化)

1. 字符串复制:Strcpy

实现逻辑

c

运行

void Strcpy(char *num,const char *src)
{
    while(*src) // 等价于 *src != '\0',遍历源字符串直到结束
    {
        *num=*src; // 源字符赋值给目标字符
        num++;     // 目标指针后移
        src++;     // 源指针后移
    }
    *num='\0'; // 手动添加字符串结束符(关键!)
}

  • 核心亮点:

    • const char *src:用const修饰源字符串指针,保证源字符串不会被意外修改,符合安全编程规范;
    • 手动添加'\0':循环仅复制有效字符,结束后必须补'\0',否则目标字符串无结束标志,后续操作会读取垃圾值。
  • 潜在问题:无目标数组容量检查,如果num数组的空间小于src字符串长度,会导致内存越界(新手需重点注意)。

2. 字符串比较:Strcmp

实现逻辑

c

运行

int Strcmp(const char *str1,const char *str2)
{
    // 循环条件:str1/str2未到结束符 + 当前字符相等
    while(*str1 && *str2&& *str1==*str2)
    {
        str1++;
        str2++;
    }
    // 返回ASCII码差值(转为unsigned char避免负数溢出)
    return (unsigned char)*str1-(unsigned char)*str2;
}

  • 核心亮点:

    • 比较规则完全符合标准库:返回<0(str1 更小)、0(相等)、>0(str1 更大),区分大小写(如 'A'(65) < 'a'(97));
    • const修饰参数,保证输入字符串不被修改。
  • 无明显逻辑问题,是 4 个函数中实现最规范的一个。

3. 字符串长度:Strlen(核心问题修复)

原代码问题

c

运行

int Strlen(const char *str)
{
    int len=0;
    while(*str)
    {
        len++;
        str++;
    }
    printf("%d\n",len); // 仅输出,无return!
}

  • 致命问题:函数声明返回int,但代码中没有return语句,违反 C 语言语法规则,编译器会报 “函数缺少返回值” 警告,运行时结果不可控;
  • 体验问题:函数内直接printf输出长度,导致测试时 “提示文字 + 函数输出” 重复,不符合 “函数只做一件事” 的原则(函数应仅返回长度,输出交给调用方)。
修复后实现

c

运行

int Strlen(const char *str)
{
    int len=0;
    while(*str != '\0') // 显式判断'\0',可读性更好
    {
        len++;
        str++;
    }
    return len; // 必须返回长度值
}

4. 字符串拼接:Strcat(核心问题修复)

实现逻辑

c

运行

void Strcat(char *num,const char *src)
{
    // 第一步:移动到目标字符串的'\0'位置
    while(*num)
    {
        num++;
    }
    // 第二步:拼接源字符串(包含'\0')
    while(*num++ = *src++);
}

  • 核心亮点:*num++ = *src++是极简写法,会逐个复制字符直到src'\0'(赋值表达式结果为'\0'时,循环终止),自动给拼接后的字符串添加结束符。
  • 原测试问题:main 函数中用str1[]="abcd"作为拼接目标,str1数组长度固定为 5(a b c d \0),拼接str2="efgh5"会导致数组越界,程序崩溃。

三、修复后的完整代码(可直接运行)

c

运行

/******************************
*文件名称:2.Strng_Copy_Compare_Length_Merge.c
*作者:czy
*邮箱:caozhiyang_0613@163.com
*创建日期:2025/12/30
*修改日期:2026/01/09
*文件功能:仿写字符串函数功能:复制,比较,求长度,以及字符串连接。
*修复点:1. Strlen添加return语句,移除内部printf;
*        2. 拼接测试目标数组扩容,避免内存越界;
*        3. 优化注释和输出格式。
*****************************/
#include<stdio.h>

/************************************************
*函数名称:Strcpy
*函数功能: 复制字符串(模拟strcpy)
*输入参数: char *num - 目标字符串(接收方);
*          const char*src - 源字符串(提供方,不可修改)
*返回参数: 无
*注意事项:需保证num数组容量足够,避免内存越界
**************************************************/
void Strcpy(char *num,const char *src)
{
    while(*src != '\0') // 遍历到源字符串结束符为止
    {
        *num = *src;    // 逐字符复制
        num++;          // 目标指针后移
        src++;          // 源指针后移
    }
    *num = '\0';        // 手动添加字符串结束符
}

/************************************************
*函数名称:Strcmp
*函数功能: 比较字符串(模拟strcmp,逐字符对比ASCII码,区分大小写)
*输入参数: const char *str1 - 待比较字符串1;
*          const char *str2 - 待比较字符串2
*返回参数: <0:str1 < str2;=0:str1 == str2;>0:str1 > str2
*注意事项:1. 字符串需以'\0'结尾;2. 区分大小写(如'A'(65) < 'a'(97))
**************************************************/
int Strcmp(const char *str1,const char *str2)
{
    // 循环条件:字符串未结束 且 当前字符相等
    while(*str1 && *str2 && *str1 == *str2)
    {
        str1++;
        str2++;
    }
    // 转为unsigned char避免负数溢出,返回ASCII码差值
    return (unsigned char)*str1 - (unsigned char)*str2;
}

/************************************************
*函数名称:Strlen
*函数功能: 获取字符串长度(模拟strlen,不含'\0')
*输入参数: const char *str - 待计算长度的字符串
*返回参数: int - 字符串的实际长度
**************************************************/
int Strlen(const char *str)
{
    int len = 0;
    while(*str != '\0') // 遍历到结束符为止
    {
        len++;
        str++;
    }
    return len; // 返回长度(核心修复!)
}

/************************************************
*函数名称:Strcat
*函数功能: 拼接字符串(模拟strcat)
*输入参数: char *num - 目标字符串(主字符串,拼接后存储);
*          const char *src - 源字符串(副字符串,不可修改)
*返回参数: 无
*注意事项:需保证num数组容量足够,避免拼接后越界
**************************************************/
void Strcat(char *num,const char *src)
{
    // 第一步:移动到目标字符串的结束符位置
    while(*num != '\0')
    {
        num++;
    }
    // 第二步:拼接源字符串(自动复制'\0')
    while(*num++ = *src++);
}

int main()
{
    char num[100];                // 复制目标数组(足够大)
    char src[] = "happyeveryday"; // 源字符串(复制用)
    char str1[100] = "abcd";      // 扩容为100字节,避免拼接越界
    char str2[] = "efgh5";        // 比较/拼接用字符串
    char str3[] = "abcd";         // 比较用字符串
    
    // 1. 复制测试
    printf("===== 1. 字符串复制测试 =====\n");
    Strcpy(num, src);
    printf("源字符串:%s\n", src);
    printf("复制结果:%s\n\n", num);
    
    // 2. 比较测试
    printf("===== 2. 字符串比较测试 =====\n");
    int cmp_result = Strcmp(str1, str2);
    if(cmp_result < 0)
    {
        printf("str1(%s) < str2(%s)\n", str1, str2);
    }
    else if(cmp_result > 0)
    {
        printf("str1(%s) > str2(%s)\n", str1, str2);
    }
    else
    {
        printf("str1(%s) == str2(%s)\n", str1, str2);
    }

    cmp_result = Strcmp(str1, str3);
    if(cmp_result == 0)
    {
        printf("str1(%s) == str3(%s)\n\n", str1, str3);
    }

    // 3. 长度测试
    printf("===== 3. 字符串长度测试 =====\n");
    printf("src的长度:%d\n", Strlen(src));
    printf("str1的长度:%d\n", Strlen(str1));
    printf("str2的长度:%d\n", Strlen(str2));
    printf("str3的长度:%d\n\n", Strlen(str3));
    
    // 4. 拼接测试
    printf("===== 4. 字符串拼接测试 =====\n");
    printf("拼接前str1:%s\n", str1);
    Strcat(str1, str2);
    printf("拼接后str1:%s\n", str1);

    return 0;
}

四、运行结果示例

plaintext

===== 1. 字符串复制测试 =====
源字符串:happyeveryday
复制结果:happyeveryday

===== 2. 字符串比较测试 =====
str1(abcd) < str2(efgh5)
str1(abcd) == str3(abcd)

===== 3. 字符串长度测试 =====
src的长度:12
str1的长度:4
str2的长度:5
str3的长度:4

===== 4. 字符串拼接测试 =====
拼接前str1:abcd
拼接后str1:abcdefgh5

五、核心知识点总结

1. 指针操作字符串的核心规则

  • 字符串的结束标志是'\0',所有遍历操作必须以*str != '\0'为终止条件;
  • 复制 / 拼接字符串后,必须保证目标字符串有'\0'(Strcpy 手动加、Strcat 自动复制);
  • const修饰源字符串指针,是避免误修改的良好编程习惯。

2. 新手常见坑点

  • Strlen 无返回值:函数声明的返回类型必须与return语句匹配,不能只输出不返回;
  • 数组越界:复制 / 拼接前需保证目标数组容量足够(如 str1 扩容为 100 字节),否则会触发内存错误;
  • 结束符丢失:手动复制字符串时,必须在末尾补'\0',否则输出会乱码。

3. 与标准库的对比

手动实现的函数逻辑与标准库<string.h>完全一致,区别在于:

  • 标准库函数做了更严格的边界检查和性能优化;
  • 新手手动实现的核心价值是理解 “指针遍历字符串” 的底层逻辑,而非替代标准库。

掌握这些指针操作字符串的核心逻辑,你就能轻松应对字符串处理的各类场景,也是后续学习文件操作、网络编程的基础。