当前位置: 首页 > news >正文

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的使用

  1.  功能:计算字符串的长度(不包括结束符 \0,也就是说必须以\0结束)
  2. 注意函数的返回值为size_t,类型是无符号的(易错)
  3. 包含头文件 <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环境下输出的结果:

 

 


完 

相关文章:

  • CSP-J 2020 入门级 第一轮 阅读程序(1)
  • 线程运行的现象和相关指令
  • python训练营day50
  • 工作总结及记录
  • 中兴B860AV1.1强力降级固件包
  • 无人设备遥控器之无线电控制技术篇
  • 大疆相机元数据说明
  • 解码成都国际数字影像产业园产城融合新发展路径
  • 【IC】Genus怎么写出scanDEF?
  • 【LangChain】2 储存
  • 三星手机Galaxy S24 Ultra使用adb工具关闭和开启系统更新
  • 小白学Pinia状态管理
  • 撤销Git合并操作方法总结
  • AI Engine Kernel and Graph Programming--知识分享9
  • 卷积神经网络(一)
  • JVM面试基础篇
  • 界面控件Kendo UI在实战应用——打通数据链路,重塑业务效率
  • leetcode0684. 冗余连接-medium
  • 3 Studying《深入理解Android卷(邓凡平)》1
  • [特殊字符] React 与 Vue 源码级对比:5大核心差异与实战选择指南
  • 网站子目录建立/网站建设知名公司
  • 白种女人做爰网站/关键词优化排名软件流量词
  • 做一样的模板网站会被告侵权吗/公司网站排名
  • 用asp.net做购物网站/廊坊优化外包
  • 如何设计制作一般的企业网站/篮网目前排名
  • 网站怎么做反向代理/成都网站seo性价比高