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

轻松拿捏C语言——【字符串函数】的使用及模拟实现

🥰欢迎关注 轻松拿捏C语言系列,来和 小哇 一起进步!✊

🎉创作不易,请多多支持🎉

🌈感谢大家的阅读、点赞、收藏和关注💕

🌹如有问题,欢迎指正 感谢


目录

一、 strlen的使用和模拟实现

二、strcpy的使用和模拟实现

三、strcat的使用和模拟实现

四、strcmp的使用和模拟实现

五、strncpy的使用和模拟实现

六、strncat的使用和模拟实现

七、strncmp的使用和模拟实现

八、strstr的使用和模拟实现

九、strtok的使用和模拟实现

十、strerror的使用和模拟实现


一、 strlen的使用和模拟实现

size_t strlen ( const char * str );

注意: 

• 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 出现前面的字符个数(不包含 '\0' )。

• 参数指向的字符串必须要以 '\0' 结束。

• 注意函数的返回值为 size_t,是无符号的( 易错 )

• strlen的使用需要包含头文件 

#include <stdio.h>
#include <string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "bbb";if(strlen(str2)-strlen(str1)>0){printf("str2>str1\n");} else{printf("srt1>str2\n");}return 0;
}

因为strlen返回值为无符号整型,所以if条件中的不是 3 - 6 = -3,而应该是-3的补码 一个大于0的数字,所以打印 str2>str1 

#include <stdio.h>
#include <string.h>
int main()
{//char arr[] = "abcdef";char arr[] = { 'a', 'b','c' };size_t len = strlen(arr);printf("%zd\n", len);return 0;
}

无\0,打印出的是随机值 

模拟实现

法一:计算器方式

//计数器⽅式
int my_strlen(const char * str)
{int count = 0;assert(str);while(*str){count++;str++;}return count;
}

法二: 递归的方式

int my_strlen(const char * str)
{assert(str);if(*str == '\0')return 0;elsereturn 1+my_strlen(str+1);
}

法三:指针相减

size_t my_strlen(const char* str)
{const char* start = str;assert(str != NULL);while (*str != '\0'){str++;}return str - start;
}

二、strcpy的使用和模拟实现

char* strcpy(char * destination, const char * source );

注意:

• 源字符串必须以 '\0' 结束。

• 会将源字符串中的 '\0' 拷贝到目标空间。

• 目标空间必须足够大,以确保能存放源字符串。

• 目标空间必须可修改。 

正确使用:

#include <stdio.h>
#include <string.h>
int main()
{char arr1[] = "hello world";char arr2[20] = {0};//1个元素strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}

错误使用:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "hello world";char* p = "xxxxxxxxxxxxxxxxxxxx";//常量字符串-不能修改strcpy(p, arr1);//errprintf("%s\n", p);return 0;
}

模拟实现: 

strcpy函数返回的是目标空间的起始地址

char* my_strcpy(char* dest, const char* src)
{//assert(dest != NULL);//assert(src != NULL);assert(dest && src);char* ret = dest;while (*src != '\0'){*dest = *src;src++;dest++;}*dest = *src;//拷贝\0return ret;//返回目标空间的起始地址
}

优化:


char* my_strcpy(char* dest, const char* src)
{//assert(dest != NULL);//assert(src != NULL);assert(dest && src);char* ret = dest;while (*dest++ = *src++)//拷贝过去字符后,判断表达式的值,当\0拷贝过去后,判断为假,停止循环{;}return ret;//返回目标空间的起始地址
}

三、strcat的使用和模拟实现

char * strcat ( char * destination, const char * source );

注意:

• 源字符串必须以 '\0' 结束。

• 目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始。

• 目标空间必须有足够的大,能容纳下源字符串的内容。

• 目标空间必须可修改。

• 字符串自己给自己追加,如何?不能保证 

使用:

int main()
{char arr1[20] = "he\0lloxxxxx";char arr2[] = "world";strcat(arr1, arr2);//字符串追加printf("%s\n", arr1);return 0;
}

从第一个\0开始追加

 

模拟实现:

#include<stdio.h>
#include<assert.h>
//要返回目标空间的起始地址
char* my_strcat(char* dest, const char* src)
{char* ret = dest;assert(dest && src);//1. 找目标空间的\0while (*dest != '\0')dest++;//2. 拷贝while (*dest++ = *src++);return ret;
}

四、strcmp的使用和模拟实现

int strcmp ( const char * str1, const char * str2 );

注意:

第⼀个字符串大于第⼆个字符串,则返回大于0的数字 ◦

第⼀个字符串等于第⼆个字符串,则返回0 ◦

第⼀个字符串小于第⼆个字符串,则返回小于0的数字 ◦

(在vs上,大于0返回的1,小于0返回的-1)

那么如何判断两个字符串? 比较两个字符串中对应位置上字符ASCII码值的大小。 

使用:

#include<stdio.h>
#include<string.h>int main()
{char arr1[] = "abq";char arr2[] = "abcdef";int ret = strcmp(arr1, arr2);//printf("%d\n", ret);if (ret >0){printf(">\n");}else{printf("<=\n");}return 0;
}

打印 >

模拟实现 :

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0'){return 0;}str1++;str2++;}return (*str1 - *str2);	
}

五、strncpy的使用和模拟实现

 char * strncpy ( char * destination, const char * source, size_t num );

注意 :

• 拷贝num个字符从源字符串到目标空间。

• 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

 

使用:

#include <stdio.h>
#include <string.h>
int main()
{char arr1[20] = "abcdef";char arr2[20] = "xxxxxxxxxxx";strncpy(arr2, arr1, 8);printf("%s\n", arr2);return 0;
}

模拟实现(如有问题,欢迎指正):

//用函数实现strncpy的功能
#include<stdio.h>
#include<assert.h>
#include<string.h>
char* my_strncpy(char* dest, char* source,size_t n)
{assert(dest && source);char* ret = dest;char* temp = source;while((*dest++ = *source++ )&&(source<temp + n)){;}return ret;
}
int main()
{char arr1[20] = "abcdef";char arr2[20] = "xxxxxxxxxx";size_t num;while(scanf("%zd", &num)!=EOF)//多次循环输入{/*strncpy(arr2, arr1, num);printf("%s\n", arr2);*/char* ret = my_strncpy(arr2, arr1, num);printf("%s\n", ret);}return 0;
}

 

六、strncat的使用和模拟实现

char * strncat ( char * destination, const char * source, size_t num );

注意:

•将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字 符。

•如果source 指向的字符串的长度小于num的时候,只会将字符串中到 \0 的内容追加到destination指向的字符串末尾 

使用:

int main()
{char arr1[20] = "abcdef\0yyyyyyyy";char arr2[20] = "xxxxxxxx";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}

模拟实现(如有问题,欢迎指正):

#include<stdio.h>
//实现strncat的功能
#include<assert.h>
char* my_strncat(char* dest,const char* source, int num)
{assert(dest && source);char* ret = dest;char* temp = source;while (*dest){dest++;}while ((*dest++ = *source++) && (source < temp+num)){;}return ret;
}
int main()
{char a[20] = "abcdef";char* b = "hhhhhh";int n;scanf("%d", &n);char* ret = my_strncat(a,b,n);printf("%s\n", ret);return 0;
}

七、strncmp的使用

int strncmp ( const char * str1, const char * str2, size_t num );

比较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字⺟,如果提前发现不⼀ 样,就提前结束,大的字符所在的字符串大于另外⼀个。如果num个字符都相等,就是相等返回0. 

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abcdef";char arr2[20] = "ab";int ret = strncmp(arr1, arr2, 4);printf("%d\n", ret);return 0;
}

八、strstr的使用和模拟实现

char * strstr ( const char * str1, const char * str2);

在str1中找str2字符串第一次出现的位置, 找到了就返回第一次出现的起始地址,找不到返回NULL;

注意: 

 函数返回字符串str2在字符串str1中第⼀次出现的位置;

字符串的比较匹配不包含 \0 字符,以 \0 作为结束标志。

使用:

#include<string.h>
int main()
{char arr[] = "abcdefabcdef";char* p = "cdefa";char* ret = strstr(arr, p);if (ret == NULL){printf("不存在\n");}else{printf("%s\n", ret);}return 0;
}

 

模拟实现(如有问题,欢迎指正):

#include<stdio.h>
//模拟实现strstr
#include<assert.h>
const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);const char* s1 = NULL;const char* s2 = NULL;const char* cur = str1;if (*str2 == '\0')return str1;while (*cur){s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2){s1++;s2++;}if (*s2 == '\0')return cur;cur++;}return NULL;
}
int main()
{char arr[] = "KIKU is JJY,an Idol and actress ";char* p = "JJY";const char* ret =my_strstr(arr, p);if (ret == NULL)printf("找不到");elseprintf("%s\n", ret);return 0;
}

九、strtok的使用

char * strtok ( char * str, const char * sep);

 注意:

• sep参数指向⼀个字符串,定义了用作分隔符的字符集合

• 第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标 记。

• strtok函数找到str中的下⼀个标记,并将其用\0 结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串⼀般都是临时拷贝的内容并且 可修改。)

• strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串 中的位置。

• strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标 记。

• 如果字符串中不存在更多的标记,则返回 NULL 指针

 

什么意思呢?通俗来讲就是,

第一次使用strtok函数时,

在第一个参数指向的字符串中去找分隔符sep,找到后把这个分隔符变成\0,

记住\0的地址即要查找的下一部分的起始地址,返回值就是\0前面这部分的地址。

然后后面再调用的话,第一个参数变成NULL,从记住的\0位置开始往后继续找分隔符,

找到了就又把这个分隔符变成\0并记住它的地址,返回值时这个\0前的这部分字符串的地址。

使用:

1、不用循环

#include <stdio.h>
#include <string.h>
//strtok函数使用
int main()
{char arr[] = "BaoGengXiaoWa@yeah.net@hehe.haha#ttttt";char buf[256] = { 0 };strcpy(buf, arr);//"BaoGengXiaoWa@yeah.net@hehe.haha#ttttt"//char sep[] = "@.";char* sep = "@.";/*char* ret = NULL;for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}*/char* ret = strtok(buf, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);return 0;
}

 

2、使用循环

#include<stdio.h>
#include<string.h>
//strtok函数使用
int main()
{char arr[] = "BaoGengXiaoWa@yeah.net@hehe.haha#ttttt";char buf[256] = { 0 };strcpy(buf, arr);//"BaoGengXiaoWa@yeah.net@hehe.haha#ttttt"//char sep[] = "@.";char* sep = "@.";char* ret = NULL;for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}/*char* ret = strtok(buf, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);*/return 0;
}

 

十、strerror的使用和模拟实现

char* strerror ( int errnum );

strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头文件中说明的

C语言程序启动的时候就会使用⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误

当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。

strerror函数就可以将错误对应的错误信息字符串的地址返回。 

我们打印⼀下0~10这些错误码对应的信息:

#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{int i = 0;for (i = 0; i <= 10; i++) {printf("%s\n", strerror(i));}return 0;
}

举例: 

#include<stdio.h>
#include <string.h>
#include <errno.h>
int main()
{//打开文件FILE* pf = fopen("data.txt", "r");//"r"- 读,以读文件的形式,打来文件,如果这个文件不存在,就打开失败if (pf == NULL){printf("打开文件失败,原因是: %s", strerror(errno));return 1;}else{printf("打开文件成功\n");//.....fclose(pf);pf = NULL;}return 0;
}

也可以使用 perror 函数,perror函数相当于⼀次将上述代码中的代码

        printf("打开文件失败,原因是: %s", strerror(errno));

完成了,直接将错误信息打印出来。

perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。 

 

int main()
{//打开文件FILE* pf = fopen("data.txt", "r");//"r"- 读,以读文件的形式,打来文件,如果这个文件不存在,就打开失败if (pf == NULL){perror("打开文件失败,原因是");//		//perror == printf + strerrorreturn 1;}else{printf("打开文件成功\n");//.....fclose(pf);pf = NULL;}return 0;
}

 🎉🎉🎉本文内容结束啦,希望各位大佬多多指教!

🌹🌹感谢大家三连支持

💕敬请期待下篇文章吧~

 

相关文章:

  • python02 循环与容器
  • DSVPN综合实验
  • 【JAVA基础之网络编程】UDP和TCP协议以及三次握手和四次挥手的过程
  • 【游戏引擎】Unity脚本基础 开启游戏开发之旅
  • Linux完整版命令大全(九)
  • Leecode热题100---55:跳跃游戏(贪心算法)
  • C++的模板(七):左值强制类型转换
  • ​Java基础复习笔记 第16章:网络编程
  • Ansible自动化运维中的Setup收集模块应用详解
  • 码蹄集部分题目(2024OJ赛16期;单调栈集训+差分集训)
  • 数据结构——栈(详细分析)
  • 渗透测试 一个很奇怪的支付漏洞
  • Day17学习Java
  • 1小时从0开始搭建自己的直播平台(详细步骤)
  • BGP策略实验
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • Effective Java 笔记(一)
  • golang 发送GET和POST示例
  • Javascript Math对象和Date对象常用方法详解
  • Javascript 原型链
  • rabbitmq延迟消息示例
  • SpiderData 2019年2月13日 DApp数据排行榜
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 分布式任务队列Celery
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 老板让我十分钟上手nx-admin
  • 聊聊flink的TableFactory
  • Hibernate主键生成策略及选择
  • 阿里云服务器如何修改远程端口?
  • 进程与线程(三)——进程/线程间通信
  • ​比特币大跌的 2 个原因
  • #《AI中文版》V3 第 1 章 概述
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (pojstep1.3.1)1017(构造法模拟)
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (万字长文)Spring的核心知识尽揽其中
  • (一)kafka实战——kafka源码编译启动
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • .net core Swagger 过滤部分Api
  • [20140403]查询是否产生日志
  • [240527] 谷歌 CEO 承认 AI 编造虚假信息问题难解(此文使用 @gemini 命令二次创作)| ICQ 停止运作
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [C++进阶篇]STL中vector的使用
  • [Django 0-1] Core.Handlers 模块
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷
  • [iOS]中字体样式设置 API
  • [java基础揉碎]方法的重写/覆盖
  • [Labtools 27-1429] XML parser encountered a problem in file
  • [LeetCode] Binary Tree Preorder Traversal 二叉树的先序遍历