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

文章目录
-
- [前景回顾:字符串专题①核心速记 📝](#前景回顾:字符串专题①核心速记 📝)
- [一、长度不受限制的字符串函数:基础但易踩坑 ⚠️](#一、长度不受限制的字符串函数:基础但易踩坑 ⚠️)
-
- [1. strcpy:字符串拷贝 📤](#1. strcpy:字符串拷贝 📤)
- [2. strcat:字符串追加 📥](#2. strcat:字符串追加 📥)
- [3. strcmp:字符串比较 🆚](#3. strcmp:字符串比较 🆚)
- [二、长度受限制的字符串函数:安全版优选 🔒](#二、长度受限制的字符串函数:安全版优选 🔒)
-
- [1. 函数对比表](#1. 函数对比表)
- [2. 实战示例:strncpy(最常用)](#2. 实战示例:strncpy(最常用))
- [3. 关键注意点](#3. 关键注意点)
- [写在最后 📝](#写在最后 📝)
字符串专题第二篇来啦!继上一篇吃透字符函数和strlen后,这一篇我们聚焦字符串操作的"三驾马车"------strcpy(拷贝)、strcat(追加)、strcmp(比较),深入拆解它们的使用规则、易错陷阱和模拟实现逻辑,同时补充更安全的长度受限版本函数,帮你彻底掌握字符串操作的核心底层逻辑!
前景回顾:字符串专题①核心速记 📝
想要吃透本篇的字符串操作函数,先回顾上一篇的关键知识点:
- 字符串的终止标志是
\0,所有库函数操作字符串均依赖\0判断结束位置。 strlen返回size_t无符号整数,直接参与减法比较易出逻辑错误。- 模拟实现库函数时,需用
assert断言指针非空,保证代码健壮性。
一、长度不受限制的字符串函数:基础但易踩坑 ⚠️
strcpy、strcat、strcmp是C语言最基础的字符串操作函数,核心特点是仅以\0作为操作终止依据,使用不当极易引发缓冲区溢出,也是笔面试中考察字符串基础的高频考点。
1. strcpy:字符串拷贝 📤
strcpy的核心作用是将源字符串完整拷贝到目标空间(包含\0),并返回目标空间起始地址以支持链式调用。
(1)函数原型与核心规则
c
char* strcpy(char* destination, const char* source);
✅ 核心规则:
- 源字符串必须以
\0结束,否则会越界拷贝内存中随机值。 - 目标空间需足够大,能容纳源字符串的所有字符(含
\0)。 - 目标空间必须可修改,不能是
const修饰的字符串或字符串常量(如char* p = "abc")。
(2)经典错误示例
c
// 错误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终止",以下是逐步优化的最终版本:
c
#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)函数原型与核心规则
c
char* strcat(char* destination, const char* source);
✅ 核心规则:
- 目标字符串和源字符串都必须以
\0结束(目标\0是追加起点)。 - 目标空间需足够大,能容纳"目标原字符串+源字符串"的总长度。
- ❌ 严禁字符串自追加!会覆盖自身
\0,导致死循环直至缓冲区溢出。
(2)自追加错误示例
c
char arr[20] = "abc";
strcat(arr, arr); // 死循环!追加时覆盖arr的'\0',无法终止
(3)模拟实现:先找\0再拷贝
c
#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)函数原型与比较规则
c
int strcmp(const char* str1, const char* str2);
✅ 比较规则(返回值):
- 若
str1 > str2→ 返回大于0的数; - 若
str1 == str2→ 返回0; - 若
str1 < str2→ 返回小于0的数。
(2)经典易错点:比较字符串地址
c
// 错误!比较的是字符串常量的内存地址,而非内容
if ("abc" > "abcd") {
printf("错误的比较逻辑\n");
}
(3)模拟实现:逐字符对比差值
c
#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语言提供了strncpy、strncat、strncmp,核心是增加size_t num参数指定最大操作长度,是实际开发中的首选。
1. 函数对比表
| 长度不受限制函数 | 长度受限制函数 | 核心差异 |
|---|---|---|
strcpy |
strncpy |
最多拷贝num个字符,源不足则补\0至num个 |
strcat |
strncat |
最多追加num个字符,追加后自动补\0 |
strcmp |
strncmp |
仅比较前num个字符 |
2. 实战示例:strncpy(最常用)
c
#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语言字符串!