一、strlen:计算字符串长度
功能说明
strlen 用于计算字符串的有效长度(不包含字符串终止符 '\\0'),区别于 sizeof(计算内存大小)。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello World";
// 输出 11(H到d共11个字符,不含'\\0')
printf("字符串长度:%zu\\n", strlen(str));
// 输出 12(包含'\\0'的内存大小)
printf("内存大小:%zu\\n", sizeof(str));
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h> // 用于空指针检查
// 手动实现strlen:指针遍历法
size_t my_strlen(const char *str) {
assert(str != NULL); // 防止传入空指针导致崩溃
const char *p = str; // 定义指针指向字符串起始位置
// 遍历直到遇到'\\0'(字符串结束标志)
while (*p != '\\0') {
p++;
}
// 指针差值即为字符串长度
return p – str;
}
// 测试代码
int main() {
char str1[] = "C Language";
char str2[] = ""; // 空字符串
// 输出 10
printf("my_strlen(\\"C Language\\") = %zu\\n", my_strlen(str1));
// 输出 0
printf("my_strlen(\\"\\") = %zu\\n", my_strlen(str2));
return 0;
}
关键逻辑解释
- assert(str != NULL):必须检查传入的字符串指针是否为空,避免访问非法内存。
- 核心逻辑:通过指针遍历字符串,直到遇到 '\\0',指针移动的总步数就是字符串长度。
- 返回值类型 size_t:是 C 语言内置的无符号整数类型,专门用于表示内存大小 / 长度。
二、strcpy:字符串拷贝
功能说明
strcpy 将源字符串(包含 '\\0')完整拷贝到目标缓冲区,要求目标缓冲区足够大,否则会导致缓冲区溢出。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[20]; // 目标缓冲区
char src[] = "Hello C";
strcpy(dest, src); // 拷贝src到dest
printf("拷贝结果:%s\\n", dest); // 输出 Hello C
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
// 手动实现strcpy:逐字符拷贝
char *my_strcpy(char *dest, const char *src) {
// 检查目标和源指针是否为空
assert(dest != NULL && src != NULL);
char *p = dest; // 保存目标缓冲区起始地址(用于返回)
// 逐字符拷贝,直到src的'\\0'(*src为'\\0'时,赋值后循环终止)
while ((*p++ = *src++) != '\\0');
return dest; // 返回目标缓冲区地址,支持链式调用
}
// 测试代码
int main() {
char dest[20];
char src[] = "String Copy";
my_strcpy(dest, src);
printf("my_strcpy结果:%s\\n", dest); // 输出 String Copy
return 0;
}
关键逻辑解释
- (*p++ = *src++) != '\\0':这是核心语句,完成 3 件事:
- 将 src 指向的字符赋值给 dest 指向的位置;
- src 和 dest 指针各向后移动一位;
- 判断赋值的字符是否为 '\\0',若是则终止循环(保证拷贝完整字符串)。
- 返回目标地址:支持 strcpy(strcpy(a, b), c) 这类链式调用。
三、strcat:字符串拼接
功能说明
strcat 将源字符串拼接到目标字符串的末尾(覆盖目标原有的 '\\0',最终添加新的 '\\0')。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[30] = "Hello, "; // 目标字符串
char src[] = "World!";
strcat(dest, src); // 拼接src到dest末尾
printf("拼接结果:%s\\n", dest); // 输出 Hello, World!
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
// 手动实现strcat:先找目标末尾,再拷贝源字符串
char *my_strcat(char *dest, const char *src) {
assert(dest != NULL && src != NULL);
char *p = dest;
// 第一步:找到目标字符串的'\\0'位置
while (*p != '\\0') {
p++;
}
// 第二步:从'\\0'位置开始拷贝源字符串(复用strcpy逻辑)
while ((*p++ = *src++) != '\\0');
return dest;
}
// 测试代码
int main() {
char dest[30] = "I love ";
char src[] = "C Programming";
my_strcat(dest, src);
printf("my_strcat结果:%s\\n", dest); // 输出 I love C Programming
return 0;
}
关键逻辑解释
- 核心分两步:先定位目标字符串的结束位置('\\0'),再从该位置开始拷贝源字符串。
- 注意事项:目标缓冲区必须预留足够空间,否则会溢出(比如 dest 数组大小要能容纳原有内容 + 源字符串内容 +'\\0')。
四、strcmp:字符串比较
功能说明
strcmp 按 ASCII 码值逐字符比较两个字符串,返回值规则:
- 返回 0:两个字符串完全相等;
- 返回 <0:第一个不同字符中,str1 的字符 ASCII 值 < str2 的;
- 返回 >0:第一个不同字符中,str1 的字符 ASCII 值 > str2 的。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Apple";
char str2[] = "Banana";
char str3[] = "Apple";
int ret1 = strcmp(str1, str2); // Apple < Banana,返回负数
int ret2 = strcmp(str1, str3); // 相等,返回0
int ret3 = strcmp(str2, str1); // Banana > Apple,返回正数
printf("strcmp(Apple, Banana) = %d\\n", ret1); // 输出 -1
printf("strcmp(Apple, Apple) = %d\\n", ret2); // 输出 0
printf("strcmp(Banana, Apple) = %d\\n", ret3); // 输出 1
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
// 手动实现strcmp:逐字符比较ASCII值
int my_strcmp(const char *str1, const char *str2) {
assert(str1 != NULL && str2 != NULL);
// 遍历直到字符不同或都到'\\0'
while (*str1 != '\\0' && *str2 != '\\0' && *str1 == *str2) {
str1++;
str2++;
}
// 返回第一个不同字符的ASCII差值(unsigned char避免负数溢出)
return (unsigned char)*str1 – (unsigned char)*str2;
}
// 测试代码
int main() {
char str1[] = "Cat";
char str2[] = "Car";
char str3[] = "Cat";
printf("my_strcmp(Cat, Car) = %d\\n", my_strcmp(str1, str2)); // t(116)-r(114)=2
printf("my_strcmp(Cat, Cat) = %d\\n", my_strcmp(str1, str3)); // 0
printf("my_strcmp(Car, Cat) = %d\\n", my_strcmp(str2, str1)); // -2
return 0;
}
关键逻辑解释
- 循环终止条件:遇到不同字符,或任一字符串到末尾('\\0')。
- (unsigned char)*str1 – (unsigned char)*str2:将字符转为无符号类型,避免 ASCII 值为 0~127 之外的字符导致负数计算错误。
五、strchr:查找字符首次出现位置
功能说明
strchr 在字符串中查找指定字符首次出现的位置,返回该位置的指针;若找不到,返回 NULL。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello World";
char *p = strchr(str, 'o'); // 查找字符'o'
if (p != NULL) {
// 输出 首次出现位置:o World(从o开始的子串)
printf("首次出现位置:%s\\n", p);
// 输出 索引:4(o在字符串中是第5个字符,索引从0开始)
printf("索引:%zu\\n", p – str);
} else {
printf("未找到字符\\n");
}
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
// 手动实现strchr:查找字符首次出现位置
char *my_strchr(const char *str, int ch) {
assert(str != NULL);
// 注意:ch会被转为unsigned char,统一字符类型
unsigned char target = (unsigned char)ch;
// 遍历字符串
while (*str != '\\0') {
if ((unsigned char)*str == target) {
return (char *)str; // 找到则返回当前指针
}
str++;
}
// 特殊情况:查找的是'\\0'(字符串结束符)
if (target == '\\0') {
return (char *)str;
}
return NULL; // 未找到返回NULL
}
// 测试代码
int main() {
char str[] = "Programming";
char *p1 = my_strchr(str, 'g');
char *p2 = my_strchr(str, 'z');
if (p1) {
printf("找到'g':%s,索引:%zu\\n", p1, p1 – str); // 输出 找到'g':gramming,索引:3
}
if (p2 == NULL) {
printf("未找到'z'\\n"); // 输出 未找到'z'
}
return 0;
}
关键逻辑解释
- int ch 参数:标准库中 strchr 的第二个参数是 int 类型(而非 char),目的是支持传入 EOF(值为 – 1),因此需要转为 unsigned char 统一处理。
- 返回指针的意义:返回的指针指向匹配字符的位置,通过 p – str 可计算出字符在字符串中的索引。
总结
这些手动实现的函数与标准库逻辑一致(简化版),你可以基于这些代码扩展功能(比如实现 strncpy、strncat 等带长度限制的安全版本)。
添加strncpy、strncat等
已完成思考
用户现在要求添加 strncpy、strncat 等字符串处理函数的源代码和实例,首先我需要先复述用户的核心需求:用户希望在之前讲解的 strlen、strcpy 等函数基础上,补充 strncpy、strncat 这两个带长度限制的安全版字符串函数的功能说明、标准库使用实例、手动实现源代码和关键逻辑解释,帮助理解这类安全版函数的底层实现和使用要点。
接下来,按照之前的结构来组织内容,先讲 strncpy,再讲 strncat,每个部分都包含功能说明、标准库实例、手动实现、关键解释,最后还要补充总结,把新增的要点加进去。
首先处理 strncpy:
- 功能说明:重点强调是带长度限制的字符串拷贝,解决 strcpy 的缓冲区溢出问题,注意如果源字符串长度小于 n,会用 '\\0' 填充到 n 个字符;如果源长度大于等于 n,目标不会自动加 '\\0',这是关键易错点。
- 标准库实例:要展示不同场景,比如源短于 n、源长于 n 的情况,突出 '\\0' 的问题。
- 手动实现:要严格遵循标准逻辑,循环 n 次,逐字符拷贝,源用完后填 '\\0',注意返回目标地址。
- 关键解释:重点讲 n 的作用,'\\0' 填充规则,以及源过长时无终止符的坑。
然后是 strncat:
- 功能说明:带长度限制的拼接,只拼接最多 n 个字符,且无论如何都会在最后加 '\\0',比 strncat 更安全,区别于 strcat 的无限制。
- 标准库实例:展示拼接 n 个字符的场景,对比源长度大于 / 小于 n 的情况。
- 手动实现:先找目标的 '\\0',然后最多拷贝 n 个字符,最后加 '\\0',注意循环条件是 n>0 且源没到 '\\0'。
- 关键解释:强调自动加 '\\0' 的特性,区别于 strncpy,以及 n 是最大拼接字符数(不含 '\\0')。
还要注意保持和之前一致的风格,代码注释清晰,实例有输出说明,关键逻辑用粗体强调,最后总结部分要补充这两个安全函数的核心要点。
现在开始组织内容,首先复述需求,然后依次讲解 strncpy、strncat,最后总结新增的关键点。
你希望在之前的基础上,补充 strncpy、strncat 这两个带长度限制的安全版字符串函数(解决 strcpy/strcat 缓冲区溢出的问题),下面为你详细讲解这两个函数的功能、标准库使用实例、手动实现源代码及核心逻辑解释。
六、strncpy:带长度限制的字符串拷贝
功能说明
strncpy 是 strcpy 的安全版本,核心是限制拷贝的最大字符数,避免缓冲区溢出。关键规则:
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
// 场景1:源字符串长度 < n
char dest1[10];
char src1[] = "Hi";
strncpy(dest1, src1, 5); // 拷贝Hi,再填3个'\\0'到总长度5
printf("场景1:dest1 = %s(长度:%zu)\\n", dest1, strlen(dest1)); // 输出 Hi(长度2)
给出完整的c语言字符串处理函数
已完成思考
用户现在需要完整的 C 语言字符串处理函数,包括之前的 strlen、strcpy、strcat、strcmp、strchr,加上 strncpy、strncat,还要补充 strncmp、strstr、strtok、memset 这些常用的,每个都要有功能说明、标准库示例、手动实现、逻辑解释,最后总结。
首先,先梳理结构,每个函数按统一的格式:功能说明、标准库使用实例、手动实现源代码、关键逻辑解释。
首先,先明确用户的核心需求:完整的常用 C 语言字符串处理函数,包含安全版(带 n 的),每个都有实现和示例,适合新手理解。
然后,开始组织内容:
然后分函数讲解:
每个部分按格式来,确保代码完整可运行,解释清晰,重点突出安全点和易错点。
然后最后总结关键点。
现在开始写具体内容,确保代码正确,解释通俗易懂,适合新手。
你需要一套完整的、常用的 C 语言字符串处理函数实现(包含基础版和安全版),下面整理了 10 个核心函数,覆盖拷贝、拼接、比较、查找、分割、内存填充等场景,每个函数都包含功能说明、标准库使用实例、手动实现源代码和关键逻辑解释,代码可直接编译运行,且标注了新手易出错的细节。
前置说明
所有字符串函数的核心前提:C 语言字符串以 '\\0'(空字符)作为结束标志,手动实现时必须围绕 '\\0' 处理,同时重点关注空指针检查和缓冲区溢出问题。
1. strlen:计算字符串长度
功能说明
计算字符串有效长度(不含 '\\0'),返回无符号整数 size_t。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello C";
printf("长度:%zu\\n", strlen(str)); // 输出 7(H到C共7个字符)
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char *str) {
assert(str != NULL); // 空指针检查,避免崩溃
const char *p = str;
while (*p != '\\0') { // 遍历到'\\0'终止
p++;
}
return p – str; // 指针差值 = 字符数
}
// 测试
int main() {
printf("%zu\\n", my_strlen("Test")); // 4
printf("%zu\\n", my_strlen("")); // 0
return 0;
}
关键逻辑
- assert(str != NULL) 是必加的安全检查;
- size_t 是无符号类型,适配内存长度的表示(避免负数)。
2. strcpy:字符串拷贝(无长度限制)
功能说明
将源字符串(含 '\\0')完整拷贝到目标缓冲区,易溢出,仅适合确定缓冲区足够大的场景。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[20];
char src[] = "String Copy";
strcpy(dest, src);
printf("拷贝结果:%s\\n", dest); // 输出 String Copy
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strcpy(char *dest, const char *src) {
assert(dest != NULL && src != NULL); // 双指针非空检查
char *p = dest; // 保存目标起始地址(用于返回)
while ((*p++ = *src++) != '\\0'); // 逐字符拷贝,包含'\\0'
return dest; // 返回目标地址,支持链式调用
}
// 测试
int main() {
char dest[20];
my_strcpy(dest, "Hello Copy");
printf("%s\\n", dest); // Hello Copy
return 0;
}
关键逻辑
- (*p++ = *src++) 是核心:先赋值、再移动指针;
- 循环终止条件是拷贝了 '\\0',保证目标字符串完整。
3. strncpy:带长度限制的字符串拷贝(安全版)
功能说明
strcpy 的安全版本,限制最大拷贝字符数 n,解决溢出问题,易错点:
- 若源字符串长度 < n:用 '\\0' 填充目标至 n 个字符;
- 若源字符串长度 ≥ n:仅拷贝前 n 个字符,目标无自动 '\\0'(需手动加)。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
// 场景1:源长度 < n
char dest1[10];
strncpy(dest1, "Hi", 5); // 拷贝"Hi" + 3个'\\0'
printf("dest1:%s(长度%zu)\\n", dest1, strlen(dest1)); // Hi(2)
// 场景2:源长度 ≥ n(手动加'\\0')
char dest2[10];
strncpy(dest2, "HelloWorld", 5); // 仅拷贝"Hello",无'\\0'
dest2[5] = '\\0'; // 必须手动补'\\0',否则会乱码
printf("dest2:%s\\n", dest2); // Hello
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strncpy(char *dest, const char *src, size_t n) {
assert(dest != NULL && src != NULL);
char *p = dest;
size_t i = 0;
// 第一步:拷贝源字符串,最多n个字符
while (i < n && *src != '\\0') {
*p++ = *src++;
i++;
}
// 第二步:源长度 < n,用'\\0'填充剩余位置
while (i < n) {
*p++ = '\\0';
i++;
}
return dest;
}
// 测试
int main() {
char dest1[10];
my_strncpy(dest1, "Test", 6);
printf("dest1:%s\\n", dest1); // Test(后补2个'\\0')
char dest2[10];
my_strncpy(dest2, "1234567890", 5);
dest2[5] = '\\0'; // 手动补'\\0'
printf("dest2:%s\\n", dest2); // 12345
return 0;
}
关键逻辑
- 分两步处理:先拷贝有效字符,再填充 '\\0'(若需要);
- 源长度 ≥ n 时,必须手动给目标加 '\\0',否则字符串无结束标志。
4. strcat:字符串拼接(无长度限制)
功能说明
将源字符串拼接到目标字符串末尾(覆盖目标原有 '\\0',最终加新 '\\0'),易溢出。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[30] = "I love ";
strcat(dest, "C Language");
printf("拼接结果:%s\\n", dest); // I love C Language
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strcat(char *dest, const char *src) {
assert(dest != NULL && src != NULL);
char *p = dest;
// 第一步:找到目标字符串的'\\0'位置
while (*p != '\\0') {
p++;
}
// 第二步:从'\\0'开始拷贝源字符串(同strcpy逻辑)
while ((*p++ = *src++) != '\\0');
return dest;
}
// 测试
int main() {
char dest[30] = "Hello, ";
my_strcat(dest, "World!");
printf("%s\\n", dest); // Hello, World!
return 0;
}
关键逻辑
- 先定位目标的 '\\0',再拼接,避免覆盖目标原有内容。
5. strncat:带长度限制的字符串拼接(安全版)
功能说明
strcat 的安全版本,限制最多拼接 n 个字符,核心优势:
- 无论源字符串多长,拼接后自动加 '\\0'(无需手动补);
- 仅拼接源字符串的前 n 个字符(不含源的 '\\0')。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello, ";
strncat(dest, "1234567890", 3); // 仅拼接前3个字符"123"
printf("拼接结果:%s\\n", dest); // Hello, 123
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strncat(char *dest, const char *src, size_t n) {
assert(dest != NULL && src != NULL);
char *p = dest;
size_t i = 0;
// 第一步:找到目标的'\\0'
while (*p != '\\0') {
p++;
}
// 第二步:拼接最多n个字符,不包含源的'\\0'
while (i < n && *src != '\\0') {
*p++ = *src++;
i++;
}
// 第三步:手动加'\\0'(核心安全点)
*p = '\\0';
return dest;
}
// 测试
int main() {
char dest[20] = "Test: ";
my_strncat(dest, "ABCDEFG", 4);
printf("%s\\n", dest); // Test: ABCD
return 0;
}
关键逻辑
- 拼接后强制加 '\\0',是 strncat 比 strncpy 更安全的核心;
- 只拼接源的前 n 个有效字符,不会拷贝源的 '\\0'。
6. strcmp:字符串比较(无长度限制)
功能说明
按 ASCII 码逐字符比较,返回值规则:
- 0:两字符串相等;
- <0:str1 小于 str2;
-
0:str1 大于 str2。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
printf("%d\\n", strcmp("Apple", "Banana")); // -1(A(65)<B(66))
printf("%d\\n", strcmp("Apple", "Apple")); // 0
printf("%d\\n", strcmp("Banana", "Apple")); // 1
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
int my_strcmp(const char *str1, const char *str2) {
assert(str1 != NULL && str2 != NULL);
// 遍历到字符不同或到'\\0'
while (*str1 != '\\0' && *str2 != '\\0' && *str1 == *str2) {
str1++;
str2++;
}
// 转为unsigned char避免负数溢出(如ASCII 0~255)
return (unsigned char)*str1 – (unsigned char)*str2;
}
// 测试
int main() {
printf("%d\\n", my_strcmp("Cat", "Car")); // 2(t(116)-r(114))
printf("%d\\n", my_strcmp("Car", "Cat")); // -2
return 0;
}
关键逻辑
- unsigned char 转换:避免字符 ASCII 值为负数时的计算错误;
- 循环终止条件:字符不同 或 任一字符串到末尾。
7. strncmp:带长度限制的字符串比较(安全版)
功能说明
最多比较前 n 个字符,避免超长字符串的无效比较。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
// 前3个字符都是"App",返回0
printf("%d\\n", strncmp("Apple", "Application", 3));
// 前2个字符"Ap"=="Ap",第3个'p'>'p'? 不,第3个'p'=='p',第4个'l'<'l'? 示例:strncmp("Apple", "App", 3) → 0
printf("%d\\n", strncmp("Apple", "App", 3)); // 0
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
int my_strncmp(const char *str1, const char *str2, size_t n) {
assert(str1 != NULL && str2 != NULL);
if (n == 0) return 0; // 比较0个字符,直接返回相等
size_t i = 0;
// 最多比较n个字符,且字符相等、未到'\\0'
while (i < n-1 && *str1 != '\\0' && *str2 != '\\0' && *str1 == *str2) {
str1++;
str2++;
i++;
}
return (unsigned char)*str1 – (unsigned char)*str2;
}
// 测试
int main() {
printf("%d\\n", my_strncmp("12345", "123678", 3)); // 0(前3个相等)
printf("%d\\n", my_strncmp("12345", "123678", 4)); // -2(4<6)
return 0;
}
关键逻辑
- i < n-1:预留最后一个字符的比较(避免越界);
- n=0 直接返回 0,符合标准库行为。
8. strchr:查找字符首次出现位置
功能说明
在字符串中查找指定字符的首次出现位置,返回该位置指针;找不到返回 NULL。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello World";
char *p = strchr(str, 'o');
if (p != NULL) {
printf("找到:%s\\n", p); // o World
printf("索引:%zu\\n", p – str); // 4
}
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strchr(const char *str, int ch) {
assert(str != NULL);
unsigned char target = (unsigned char)ch; // 统一字符类型
while (*str != '\\0') {
if ((unsigned char)*str == target) {
return (char *)str; // 找到返回指针
}
str++;
}
// 特殊情况:查找的是'\\0'
if (target == '\\0') {
return (char *)str;
}
return NULL; // 未找到
}
// 测试
int main() {
char *p = my_strchr("Programming", 'g');
printf("%s\\n", p); // gramming
return 0;
}
关键逻辑
- 参数 ch 是 int 类型:支持传入 EOF(值为 – 1),需转为 unsigned char;
- 支持查找 '\\0'(返回字符串末尾指针)。
9. strstr:查找子串首次出现位置
功能说明
在字符串 str 中查找子串 sub 的首次出现位置,返回指针;找不到返回 NULL。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "I love C Programming";
char *p = strstr(str, "Programming");
if (p != NULL) {
printf("找到子串:%s\\n", p); // Programming
}
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
char *my_strstr(const char *str, const char *sub) {
assert(str != NULL && sub != NULL);
if (*sub == '\\0') return (char *)str; // 空子串返回原字符串
const char *p_str = str;
const char *p_sub;
// 遍历原字符串
while (*p_str != '\\0') {
p_sub = sub;
const char *temp = p_str;
// 匹配子串
while (*temp != '\\0' && *p_sub != '\\0' && *temp == *p_sub) {
temp++;
p_sub++;
}
// 子串全部匹配完成
if (*p_sub == '\\0') {
return (char *)p_str;
}
p_str++;
}
return NULL; // 未找到
}
// 测试
int main() {
char *p = my_strstr("HelloWorldHello", "World");
printf("%s\\n", p); // WorldHello
return 0;
}
关键逻辑
- 双层循环:外层遍历原字符串,内层匹配子串;
- 空子串(*sub == '\\0')直接返回原字符串(标准库规则)。
10. strtok:字符串分割(按分隔符拆分)
功能说明
按指定分隔符(如 ,、)将字符串拆分为多个子串,注意:
- 会修改原字符串(将分隔符替换为 '\\0');
- 首次调用传原字符串,后续调用传 NULL。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Apple,Banana,Cherry"; // 必须是可修改的数组(不能是常量字符串)
char *token = strtok(str, ","); // 首次调用:传原字符串+分隔符
while (token != NULL) {
printf("分割结果:%s\\n", token); // Apple → Banana → Cherry
token = strtok(NULL, ","); // 后续调用:传NULL
}
return 0;
}
手动实现源代码(简化版)
c
运行
#include <stdio.h>
#include <assert.h>
#include <string.h>
// 静态变量保存上次分割的位置
static char *last_pos = NULL;
char *my_strtok(char *str, const char *delim) {
assert(delim != NULL);
if (str == NULL) {
str = last_pos; // 后续调用:从上次位置继续
if (str == NULL) return NULL; // 无剩余字符
}
// 第一步:跳过开头的分隔符
while (*str != '\\0' && strchr(delim, *str) != NULL) {
str++;
}
// 无有效字符
if (*str == '\\0') {
last_pos = NULL;
return NULL;
}
// 第二步:找到分隔符位置,替换为'\\0'
char *token_start = str;
while (*str != '\\0' && strchr(delim, *str) == NULL) {
str++;
}
if (*str != '\\0') {
*str = '\\0'; // 分隔符替换为'\\0'
last_pos = str + 1; // 保存下次分割的起始位置
} else {
last_pos = NULL; // 到末尾,下次返回NULL
}
return token_start;
}
// 测试
int main() {
char str[] = "123 456 789";
char *token = my_strtok(str, " ");
while (token != NULL) {
printf("%s\\n", token); // 123 → 456 → 789
token = my_strtok(NULL, " ");
}
return 0;
}
关键逻辑
- static char *last_pos:保存上次分割的位置,实现 “后续调用传 NULL” 的逻辑;
- 原字符串必须是可修改的(不能是 const char *),否则会崩溃。
11. 辅助函数:memset(内存填充,初始化字符串)
功能说明
将指定内存区域填充为指定字符(常用于字符串初始化,如清空缓冲区)。
标准库使用实例
c
运行
#include <stdio.h>
#include <string.h>
int main() {
char dest[20];
memset(dest, 0, sizeof(dest)); // 全部填充为'\\0'
strcpy(dest, "Test");
printf("%s\\n", dest); // Test
memset(dest, '*', 5); // 前5个字符填充为'*'
printf("%s\\n", dest); // *****
return 0;
}
手动实现源代码
c
运行
#include <stdio.h>
#include <assert.h>
void *my_memset(void *ptr, int value, size_t num) {
assert(ptr != NULL);
unsigned char *p = (unsigned char *)ptr;
unsigned char val = (unsigned char)value;
while (num– > 0) {
*p++ = val;
}
return ptr; // 返回原指针,支持链式调用
}
// 测试
int main() {
char dest[20];
my_memset(dest, 0, sizeof(dest));
my_memset(dest, '#', 3);
printf("%s\\n", dest); // ###
return 0;
}
总结
- 所有函数必须先检查指针是否为 NULL(assert 或条件判断);
- 带 n 的函数(strncpy/strncat/strncmp)优先使用,避免缓冲区溢出;
- strncpy 需手动补 '\\0',strncat 自动补 '\\0'(更安全)。
- C 字符串以 '\\0' 结尾,所有处理逻辑必须围绕 '\\0';
- 指针操作是字符串函数的核心,理解 *p++ 等操作是关键;
- strtok 会修改原字符串,使用时需注意原字符串的可修改性。
- 实际开发中优先用标准库函数(经过严格测试);
- 手动实现仅用于理解底层逻辑,生产环境避免重复造轮子。
网硕互联帮助中心




评论前必须登录!
注册