C语言——深入解析字符串函数与其模拟实现
目录
一、strlen的使用和模拟实现
1.1 strlen的使用
1.2 strlen模拟实现
二、strcpy 的使用和模拟实现
2.1 strcpy的使用
三、strcat函数的使用和模拟实现
3.1 strcat模拟实现
四、strcmp的使用和模拟实现
4.1 strcmp使用:
4.2 strcmp模拟实现:
五 、strstr的使用和模拟实现
5.1 strstr使用
5.2 strstr模拟
六、strtok的使用和模拟实现
七、strerror函数
在日常编程中,我们常常会遇到这样的问题:如何计算字符串的长度?如何追加一个已经初始化的字符串内容?如何将字符串的大小?这些看似简单的操作,实际上却涉及到一系列强大的字符串函数。本文将带您深入了解这些函数的使用方法和背后的原理。
本文主要通过字符串的实现以及通过自定义函数来模拟实现字符串功能来帮助大家更好的理解以下笔者举出的字符串函数
- strlen —— 计算字符串的长度
- strcpy——将源字符串复制到目标字符串。
- strcat——将源字符串追加到目标字符串的末尾。
- strcmp—— 比较两个字符串的大小。
- strstr——查找一个字符串在另一个字符串中的首次出现位置。
- strtok——将字符串分割成一系列标记(tokens),通常用于解析字符串。
- strerror—— 返回与错误码对应的错误信息字符串。
一、strlen的使用和模拟实现
1.1 strlen的使用
- 功能:计算字符串的长度(不包括结束符
\0,也就是说必须以\0结束)
注意函数的返回值为size_t,类型是无符号的(易错)
包含头文件 <string.h>
size_t strlen(const char *str);
//类型size_t
由于strlen笔者在之前的博客有过讲解,这里直接模拟实现
1.2 strlen模拟实现
//计数器
#include <assert.h>
int my_strlen(const char* str)
{int count = 0;assert(str);//断言 确保str不为NULLwhile (*str){count++;str++;}return count;
}//方式二int main()
{const char* str = "abcdef";int r= my_strlen(str);printf("%d\n", r);return 0;}
在这我仔细讲一下指针-指针的方式
#include <stdio.h>
#include <assert.h>int my_strlen(const char* str) {assert(str);const char* p = str; // 使用 const char* 以保持一致性while (*p != '\0') {p++;}return p - str; // 返回长度
}int main() {const char* str = "abcdef";int r = my_strlen(str); // 修改为 int 类型printf("%d\n", r);return 0;
}
- return p-str:
- 在
my_strlen
函数中,p
最初指向字符串的起始位置(即str
),然后通过while
循环遍历字符串,直到遇到字符串的结束符'\0'
。此时,p
指向'\0'
的位置。 p - str
计算的是p
和str
之间的距离(以字符为单位)。因为p
指向字符串的结束符,而str
指向字符串的起始位置,所以p - str
的结果就是字符串的长度。
假设字符串是
"abcdef"
,它在内存中的布局如下:地址: 0x1000 0x1001 0x1002 0x1003 0x1004 0x1005 0x1006 内容: 'a' 'b' 'c' 'd' 'e' 'f' '\0'
str
指向0x1000
(即字符'a'
)。- 当
p
在循环结束时指向0x1006
(即字符'\0'
)。- 计算
p - str
:
0x1006 - 0x1000 = 6
,这就是字符串的长度。因此,
return p - str;
返回的是字符串的长度。
二、strcpy 的使用和模拟实现
2.1 strcpy的使用
char *strcpy(char *dest, const char *src);
dest(destination)
是目标字符串的指针,复制的内容将存储在这里。src
(source)是源字符串的指针,内容将从这里复制。strcpy
函数会将src
字符串中的字符逐个复制到dest
中,直到遇到字符串结束符'\0'
,然后在dest
的末尾也添加一个'\0'
。- 注意:目标的空间一定要大,以确保能存放源字符串
- 目标空间必须可修改
#include <stdio.h>
#include <string.h>int main() {char src[] = "Hello, World!";char dest[50]; // 确保目标数组足够大以容纳源字符串strcpy(dest, src); // 复制字符串printf("源字符串: %s\n", src);printf("目标字符串: %s\n", dest);return 0;
}
2.2 模拟实现
char* my_strcpy(char* dest, const char* src)
{char* ret = dest;assert(dest != NULL && src != NULL);while ((*dest++ = *src++)){;}return ret;
}int main() {char src[] = "Hello, World!";char dest[50]; // 确保目标数组足够大以容纳源字符串my_strcpy(dest, src); // 使用自定义的 strcpy 函数printf("源字符串: %s\n", src);printf("目标字符串: %s\n", dest);return 0;
}
while ((*dest++ = *src++));
1. 表达式的结构
*dest++ = *src++
是一个赋值表达式,包含了以下几个部分:
*src++
: 先获取src
指针指向的字符,然后将src
指针向后移动一个位置。*dest++
: 先获取dest
指针指向的位置,然后将src
中的字符赋值给这个位置,最后将dest
指针向后移动一个位置。2. 逐步解析
*src++
:
*src
:解引用src
指针,获取当前指向的字符。src++
:在获取字符后,将src
指针向后移动到下一个字符。
*dest++
:
*dest
:解引用dest
指针,获取当前指向的目标位置。dest++
:在赋值后,将dest
指针向后移动到下一个位置。赋值:
*dest++ = *src++
:将src
中的当前字符赋值给dest
中的当前字符(赋值的顺序是从右到左),并同时更新两个指针。3. 循环条件
while ((*dest++ = *src++))
:
- 这个
while
循环会持续执行,直到*src
指向的字符为'\0'
(字符串结束符)。- 当
*src
为'\0'
时,赋值表达式的结果为0
(因为'\0'
的 ASCII 值为 0),因此while
循环结束。4. 整体功能
- 这个
while
循环的整体功能是:
- 从
src
中逐个字符复制到dest
,直到遇到字符串结束符'\0'
。- 在复制的过程中,
dest
和src
指针都会向后移动,以便指向下一个字符。5. 返回值
- 循环结束后,
my_strcpy
函数会返回ret
,即最初的dest
指针,指向目标字符串的起始位置。
三、strcat函数的使用和模拟实现
const char *strcat(char *dest,char *scr);
dest(destination)
是目标字符串的指针,追加的内容将存储在这里。src
(source)是源字符串的指针,内容将从这里追加。strcpy
函数会将src
字符串中的字符追加到dest
中,直到遇到字符串结束符'\0'
,然后在dest
的末尾也添加一个'\0'
。- 注意:目标的空间一定要大,以确保能存放源字符串
- 目标空间必须可修改
3.1 strcat模拟实现
由于strcat函数和strcpy函数类似,这里直接开始模拟实现,可参照strcpy
#include <stdio.h>
#include <assert.h>char* my_strcat(char* dest, const char* src) {assert(dest != NULL && src != NULL); // 确保指针不为 NULL// 找到目标字符串的末尾char* ret = dest; // 保存目标字符串的起始地址while (*dest) { // 遍历目标字符串,直到遇到 \'\\0\'dest++;}// 复制源字符串到目标字符串的末尾while ((*dest++ = *src++)) { // 逐个字符复制;}return ret; // 返回目标字符串的起始地址
}int main() {char dest[20] = "Hello"; // 目标字符串const char* src = "World!"; // 源字符串my_strcat(dest, src); // 使用自定义的 strcat 函数printf("%s,\n", dest); // 输出: Hello, World!return 0;
}
这里的 while (*dest) {
dest++; }
- 由于我们是追加函数,实现在末尾追加源字符串的内容的话,我们肯定是要将指针指向‘\0’,否则会出现覆盖的情况
例如:
- 假设
dest
原本是"Hello"
,而src
是" World"
。- 如果不移动
dest
,那么在复制时,dest
会从"H"
开始覆盖,最终结果将是" World"
,而不是"Hello World"
。
四、strcmp的使用和模拟实现
标准规定:
参数:
str1
:指向第一个字符串的指针。str2
:指向第二个字符串的指针。返回值:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
int strcmp(const char *str1, const char *str2);
4.1 strcmp使用:
int main()
{const char* str1 = "abcdef";const char* str2 = "abcdg";int result = strcmp(str1, str2);if (result < 0) {printf("%s is less than %s\n", str1, str2);}else if (result > 0) {printf("%s is greater than %s\n", str1, str2);}else {printf("%s is equal than %s\n", str1, str2);}return 0;
}
4.2 strcmp模拟实现:
int my_strcmp(const char* str1, const char* str2)
{int ret = 0;assert(str1 != NULL && str1 != NULL);//判断是否为\0以及两个字符串所指向的值是否相等while (*str1 && (*str1 == *str2)) {str1++;str2++;}return *(unsigned char*)str1 - *(unsigned char*)str2;}
int main()
{const char* str1 = "abcdef";const char* str2 = "abcdg";int result = my_strcmp(str1, str2);if (result < 0) {printf("%s is less than %s\n", str1, str2);}else if (result > 0) {printf("%s is greater than %s\n", str1, str2);}else {printf("%s is equal than %s\n", str1, str2);}return 0;
}
字符串比较:
- 使用
while
循环逐个比较str1
和str2
中的字符。*str1
和*str2
分别表示当前字符。如果当前字符相等且*str1
不为'\0'
(即字符串未结束),则继续比较下一个字符。- 在每次循环中,指针
str1
和str2
都向后移动一个字符。返回比较结果:
- 当循环结束时,
str1
和str2
指向的字符要么是不同的字符,要么是字符串的结束符'\0'
。- 使用
*(unsigned char*)str1
和*(unsigned char*)str2
获取当前字符的 ASCII 值,并计算它们的差值。- 返回值的意义:
- 如果
str1
小于str2
,返回一个负值。- 如果
str1
等于str2
,返回 0。- 如果
str1
大于str2
,返回一个正值。
五 、strstr的使用和模拟实现
- 功能:函数返回字符串str2在字符串str1的出现的位置
- 字符串的比较匹配不包含‘\0’,以‘\0’为结束标志
char * strstr(const char *str1,const char *str2);
str1——要搜索的主字符串;
str2——要查找的子字符串;
5.1 strstr使用
#include <stdio.h>
#include <string.h>int main() {const char* haystack = "Hello, world!";const char* needle = "world";char* result = strstr(haystack, needle);if (result) {printf("Found '%s' at position: %ld\n", needle, result - haystack);}else {printf("'%s' not found in '%s'\n", needle, haystack);}return 0;
}
5.2 strstr模拟
#include <stdio.h>// 自定义 strstr 函数,用于在 str1 中查找 str2 的首次出现
char* strstr(const char* str1, const char* str2)
{char* cp = (char*)str1; // cp 指向 str1 的起始位置char* s1, * s2; // s1 用于遍历 str1,s2 用于遍历 str2// 如果 str2 为空字符串,直接返回 str1if (!*str2)return((char*)str1);// 遍历 str1while (*cp){s1 = cp; // s1 指向当前 cp 位置s2 = (char*)str2; // s2 指向 str2 的起始位置// 比较 s1 和 s2 指向的字符,直到遇到不同的字符或到达字符串末尾while (*s1 && *s2 && !(*s1 - *s2))s1++, s2++; // 同时移动 s1 和 s2// 如果 s2 到达了字符串末尾,说明找到了 str2if (!*s2)return (cp); // 返回 str1 中 str2 的起始位置cp++; // 移动 cp,继续查找}// 如果没有找到 str2,返回 NULLreturn (NULL);
}int main() {const char *haystack = "Hello, world!"; // 要搜索的字符串const char *needle = "world"; // 要查找的子字符串// 调用自定义 strstr 函数char *result = strstr(haystack, needle);// 检查结果并输出if (result) {printf("Found '%s' at position: %ld\n", needle, result - haystack); // 输出找到的位置} else {printf("'%s' not found in '%s'\n", needle, haystack); // 输出未找到的提示}return 0; // 程序结束
}
六、strtok的使用和模拟实现
char* strtok (char * str,const char* sep);
//sep参数指向一个字符串,定义了用分隔符的字符集合
//str制定了一个字符串
- strtok函数是用于 “将字符串分割成一系列标记”
- strtok函数找到str中的下一个标记,会讲其用‘\0’结尾 ,返回一个指向这个标记的指针
例如:
原:123456@abc.com
改后:123456\0abc.com
- strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存他在字符串中的位置
- strtok函数的第一个参数为NUNLL,函数将在同一字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回NULL指针
#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, world! Welcome to C programming.";const char sep[] = " ,.!"; // 定义分隔符char *token;// 获取第一个子字符串token = strtok(str, sep);// 继续获取后续的子字符串while (token != NULL) {printf("Token: %s\n", token); // 输出当前的子字符串token = strtok(NULL, sep); // 获取下一个子字符串}return 0; // 程序结束
}
七、strerror函数
strerror函数用于“返回与错误码对应的错误信息字符串”
char *strror(int errnum);//errnum为错误码的意思,
//这个错误码通常是由系统调用或库函数返回的。
需要包含头文件<errno.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>int main() {FILE *file = fopen("non_existent_file.txt", "r");if (file == NULL) {// 打印错误码printf("Error opening file: %d\n", errno);// 使用 strerror 获取错误信息printf("Error message: %s\n", strerror(errno));} else {fclose(file);}return 0;
}
在这里笔者会用一种方式让大家看明白strerror函数的返回
#include<errno.h>
#include<string.h>
#include<stdio.h>
int main()
{int i;for (i = 0;i <= 10;i++){printf("%s\n",strerror(i));}return 0;
}
在win11+vs2022环境下输出的结果:
完