在这里插入图片描述

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言数据结构(C语言)EasyX游戏规划
✨ 从来绝巘须孤往,万里同尘即玉京

在这里插入图片描述

字符串专题第二篇来啦!继上一篇吃透字符函数和strlen后,这一篇我们聚焦字符串操作的“三驾马车”——strcpy(拷贝)、strcat(追加)、strcmp(比较),深入拆解它们的使用规则、易错陷阱和模拟实现逻辑,同时补充更安全的长度受限版本函数,帮你彻底掌握字符串操作的核心底层逻辑!

前景回顾:字符串专题①核心速记 📝

C 语言字符串入门:字符函数 + strlen 精讲(从使用到模拟实现)

想要吃透本篇的字符串操作函数,先回顾上一篇的关键知识点:

  1. 字符串的终止标志是\0,所有库函数操作字符串均依赖\0判断结束位置。
  2. strlen返回size_t无符号整数,直接参与减法比较易出逻辑错误。
  3. 模拟实现库函数时,需用assert断言指针非空,保证代码健壮性。

一、长度不受限制的字符串函数:基础但易踩坑 ⚠️

strcpystrcatstrcmp是C语言最基础的字符串操作函数,核心特点是仅以\0作为操作终止依据,使用不当极易引发缓冲区溢出,也是笔面试中考察字符串基础的高频考点。

1. strcpy:字符串拷贝 📤

strcpy的核心作用是将源字符串完整拷贝到目标空间(包含\0),并返回目标空间起始地址以支持链式调用。

(1)函数原型与核心规则
char* strcpy(char* destination, const char* source);

✅ 核心规则:

  • 源字符串必须以\0结束,否则会越界拷贝内存中随机值。
  • 目标空间需足够大,能容纳源字符串的所有字符(含\0)。
  • 目标空间必须可修改,不能是const修饰的字符串或字符串常量(如char* p = "abc")。
(2)经典错误示例
// 错误1:目标空间是字符串常量,不可修改
char* dest = "xxxxxxxxxx";
char* src = "abcdef";
strcpy(dest, src); // 运行崩溃

// 错误2:源字符串无'\0',越界拷贝
char arr1[] = {'a','b','c'}; // 无'\0'
char arr2[5] = {0};
strcpy(arr2, arr1); // 拷贝随机值,缓冲区溢出
(3)模拟实现:从基础到精简

模拟实现的核心是“逐字符拷贝+\0终止”,以下是逐步优化的最终版本:

#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* dest, const char* src) {
    assert(dest && src); // 断言空指针,提升代码健壮性
    char* ret = dest;    // 保存目标起始地址,用于返回
    
    // 后置++:先拷贝字符,再移动指针;'\0'的ASCII为0,拷贝后循环终止
    while (*dest++ = *src++) {
        ; // 空语句,核心逻辑在循环条件中
    }
    
    return ret; // 返回目标起始地址,支持链式调用
}

// 测试代码
int main() {
    char arr1[] = "abcdef";
    char arr2[20] = "xxxxxxxxxx";
    printf("%s\n", my_strcpy(arr2, arr1)); // 输出:abcdef
    return 0;
}

2. strcat:字符串追加 📥

strcat用于将源字符串追加到目标字符串的末尾,追加起始位置是目标字符串的\0处,同样会拷贝源字符串的\0

(1)函数原型与核心规则
char* strcat(char* destination, const char* source);

✅ 核心规则:

  • 目标字符串和源字符串都必须以\0结束(目标\0是追加起点)。
  • 目标空间需足够大,能容纳“目标原字符串+源字符串”的总长度。
  • ❌ 严禁字符串自追加!会覆盖自身\0,导致死循环直至缓冲区溢出。
(2)自追加错误示例
char arr[20] = "abc";
strcat(arr, arr); // 死循环!追加时覆盖arr的'\0',无法终止
(3)模拟实现:先找\0再拷贝
#include <stdio.h>
#include <assert.h>

char* my_strcat(char* dest, const char* src) {
    assert(dest && src);
    char* ret = dest;
    
    // 步骤1:找到目标字符串的'\0'
    while (*dest) {
        dest++;
    }
    
    // 步骤2:从'\0'开始拷贝源字符串(同strcpy逻辑)
    while (*dest++ = *src++) {
        ;
    }
    
    return ret;
}

// 测试代码
int main() {
    char arr1[20] = "hello ";
    char arr2[] = "world";
    printf("%s\n", my_strcat(arr1, arr2)); // 输出:hello world
    return 0;
}

3. strcmp:字符串比较 🆚

strcmp用于按ASCII码值逐字符比较两个字符串的内容,绝对不能直接用>/</==比较字符串地址

(1)函数原型与比较规则
int strcmp(const char* str1, const char* str2);

✅ 比较规则(返回值):

  • str1 > str2 → 返回大于0的数;
  • str1 == str2 → 返回0;
  • str1 < str2 → 返回小于0的数。
(2)经典易错点:比较字符串地址
// 错误!比较的是字符串常量的内存地址,而非内容
if ("abc" > "abcd") {
    printf("错误的比较逻辑\n");
}
(3)模拟实现:逐字符对比差值
#include <stdio.h>
#include <assert.h>

int my_strcmp(const char* str1, const char* str2) {
    assert(str1 && str2);
    
    // 逐字符比较,直到字符不同或遇到'\0'
    while (*str1 == *str2) {
        // 同时到'\0',说明字符串完全相等
        if (*str1 == '\0') {
            return 0;
        }
        str1++;
        str2++;
    }
    
    // 返回不同字符的ASCII差值,直接体现大小关系
    return *str1 - *str2;
}

// 测试代码
int main() {
    int ret = my_strcmp("abcdef", "abc");
    if (ret > 0) {
        printf("abcdef > abc\n"); // 输出此结果
    } else if (ret == 0) {
        printf("abcdef == abc\n");
    } else {
        printf("abcdef < abc\n");
    }
    return 0;
}

二、长度受限制的字符串函数:安全版优选 🔒

为解决长度不受限制函数的缓冲区溢出问题,C语言提供了strncpystrncatstrncmp,核心是增加size_t num参数指定最大操作长度,是实际开发中的首选。

1. 函数对比表

长度不受限制函数 长度受限制函数 核心差异
strcpy strncpy 最多拷贝num个字符,源不足则补\0num
strcat strncat 最多追加num个字符,追加后自动补\0
strcmp strncmp 仅比较前num个字符

2. 实战示例:strncpy(最常用)

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

int main() {
    char arr1[] = "abcdef";
    char arr2[20] = "xxxxxxxxxx";
    
    // 仅拷贝前3个字符,剩余位置保留原内容
    strncpy(arr2, arr1, 3);
    printf("%s\n", arr2); // 输出:abcxxxxxxx
    
    // 源字符串长度小于num,补'\0'至num个
    char arr3[10] = "123456789";
    strncpy(arr3, "xyz", 5);
    printf("%s\n", arr3); // 输出:xyz\0\0789
    return 0;
}

3. 关键注意点

  • strncpy:若源字符串长度大于num,仅拷贝num个字符,不会自动补\0,需手动添加。
  • strncat:无论源字符串多长,追加后都会补\0,安全性最高,推荐优先使用。

写在最后 📝

本篇聚焦的strcpy/strcat/strcmp是字符串操作的基础,掌握它们的模拟实现,能帮你深入理解指针和内存操作的底层逻辑;而长度受限版本的函数,则是工程实践中规避缓冲区溢出的关键。

这些知识点不仅是日常开发的必备技能,更是笔面试中“手写库函数”类考题的核心考点,建议大家动手敲一遍代码,加深对逻辑的理解。

下一篇,我们将讲解字符串操作的高阶函数——strstr(子串查找)、strtok(字符串分割)、strerror(错误处理),覆盖字符串操作的全场景,帮你彻底吃透C语言字符串!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐