C 语言实战:手动实现字符串核心函数(指针版)
字符串操作是 C 语言的核心技能之一,标准库<string.h>中的strcpy、strcmp、strlen、strcat是最常用的字符串函数。本文基于你提供的代码,拆解手动实现这 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>完全一致,区别在于:
- 标准库函数做了更严格的边界检查和性能优化;
- 新手手动实现的核心价值是理解 “指针遍历字符串” 的底层逻辑,而非替代标准库。
掌握这些指针操作字符串的核心逻辑,你就能轻松应对字符串处理的各类场景,也是后续学习文件操作、网络编程的基础。