C语言字符函数和字符串函数(20)

文章目录

  • 前言
  • 一、字符分类函数
    • 小练习
  • 二、字符转换函数
  • 三、strlen的使用和模拟实现
  • 四、strcpy的使用和模拟实现
  • 五、strcat的使用和模拟实现
  • 六、strcmp的使用和模拟实现
  • 七、strncpy函数的使用
  • 八、strncat函数的使用
  • 九、strncmp函数的使用
  • 十、strstr函数的使用和模拟实现
  • 十一、strtok的使用
  • 十二、strerror的使用
  • 总结


前言

  开启新阶段的学习了!大家加油!


一、字符分类函数

  C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的

它们的使用都需要包含一个头文件是ctype.h

在这里插入图片描述
我们就以一个函数为例,注意这个函数:int islower (int c);
作用很明显,判断c是否是小写字母的,通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0

小练习

请编写一个程序,将字符串中的小写字母转大写,其他字符保持不变

应该说思路还是挺明显的,遍历一遍字符串,加个是否是小写的判断条件(islower),减个32,就可以了( ‘a’ 与 ‘A’ 差32)

二、字符转换函数

在这里插入图片描述
  前面那个代码,我们想着通过-32来实现大写到小写的转换,其实我们还可以考虑上面两个函数,值得注意的是,虽然说是转换,但是如果本来就是大写的话,toupper也可以接收,只是没啥效果罢了~

你也去试试吧!

三、strlen的使用和模拟实现

函数原型:size_t strlen(const char* str);

使用注意事项:
1.字符串以’\0’作为结束标志,strlen函数返回的是字符串中’\0’前面出现的字符个数(不包含’\0’)
2.参数指向的字符串必须要以’\0’结束
3.注意函数的返回值为size_t,是无符号的
4.strlen的使用需要包含头文件

  对于strlen,我们也来尝试实现以下我们自己的my_strlen,因为我们本质上不希望改动所接收的字符串,所以加个const修饰
  接下来我们思考如何求出字符串的长度,逻辑其实也很简单,字符串以’\0’结尾,那就从头遍历,遍历一个字符,计数器加一,代码如下:
在这里插入图片描述
  至于说完善一下,因为循环条件有解引用,所以我们要预防一下空指针,于是加个断言

assert(str != NULL);

  同样的,我们回顾前面指针 - 指针得到的是元素个数这个特点,我们可以接收完指针后,设置一个临时变量start保存起始位置,再让str后移到’\0’,接着再str - start,这种思路也是极棒的,代码如下:
在这里插入图片描述

那如果不能使用临时变量呢?那就考虑递归,这也是前面讲过的!
请看下面代码(突然发现还有这个代码块…好吧,是我之前没了解全面,这样应该就方便复制了)

#include <stdio.h>  
#include <string.h>size_t my_strlen(char* str)
{if (str == '\0')return 0;return 1 + my_strlen(str + 1);
}int main()
{char* str = "abcdef";printf("%zd\n", strlen(str));return 0;
}

四、strcpy的使用和模拟实现

函数原型:char* strcpy(char* destination, const char* source);

使用注意事项:
1.源字符串必须以’\0’结束
2.会将源字符串的’\0’拷贝到目标空间
3.目标空间必须足够大,以确保能存放源字符串
4.目标空间必须可修改
5.同样也得包含头文件

我们来确认一下第2点,看看到底会不会把字符0复制过来

这很简单,我们给arr1塞点东西进去
char arr1[20] = “xxxxxxxxx”;
char arr2[] = “hello”;

打开监视,并且观察strcpy前后arr1的变化,会发现显然把字符0也给复制过去了
在这里插入图片描述
在这里插入图片描述

我们再来看下第二点,因为它会将源字符串的起始位置到字符0全部复制给arr1,可能会导致越界:
在这里插入图片描述

至于说目标空间必须足够大,理由自不必说,只是我们需要考虑字符0也算,另外目标字符串不能是常量字符串,因为它不可修改

接下来我们来研究一下strcpy的模拟实现,显然my_strcpy不需要返回类型,我们来思考一下复制的逻辑,显然 arr1 和 arr2 一个字符一个字符的循环复制,可是循环条件是什么呢?
我们思考,src指针一直到 ‘\0’ 才停下来,所以我们条件是当 src != ‘\0’ 的时候循环,可是’\0’也是需要复制的,于是我们在循环后面再赋值一次 *dest = *src; 即可

可是我们到底是不太满意的,为了这最后一次,还要再单独写一个语句,这很不好,可以想一下有没有其他的好方法,我们可以把 *dest++ = *src; 放到while里面

while (*dest++ = *src++) 相当巧妙,刚好到’\0’的时候就复制,然后停下
代码如下:
在这里插入图片描述

插一条小知识
NULL本质上也是0,一般用于指针的初始化
\0是\ddd形式的转义字符,本质也是0,一般字符串的末尾会有\0,是字符串的结束标志
0是数字0
null NUL -> \0
'0’是字符0,本质是48

五、strcat的使用和模拟实现

函数原型:char* strcat(char* destination, const char* source);

原理是找到目标字符串的\0,然后开始拷贝
注意事项有:
1.源字符串必须以’\0’结束
2.目标字符串中也得有\0,否则没办法知道追加从哪里开始
3.目标空间必须足够的大,能容纳下源字符串的内容
4.目标空间可修改
5.字符串自己给自己追加,如何?

  我们来思考一下模拟实现my_strcat
  首先找到目标空间的\0,这还是比较简单的,*dest自加循环到\0即可
  然后开始拷贝,然后开始拷贝,这时候又可以 while(*dest++ = *src) 了,代码如下:
在这里插入图片描述

如果 my_strcat(arr1, arr2); 是不安全的,因为会死循环,永远找不到\0
在这里插入图片描述

六、strcmp的使用和模拟实现

函数原型:int strcmp(const char* str1, const char* str2);
作用是比较对应位置上的字符大小,小的字符所在的字符串,小于另外一个字符串,而不是字符串的长度!

  我们来模拟实现一下 my_strcmp ,首先我们不希望 str1 和 str2 被修改,因此要加个 const ,接着我们来思考一下比较逻辑,首先获得两个字符串的起始位置,一直循环判断到两个指针所指向的字符不一样,所以有 while(*s1 == *s2),期中,我们考虑到如果两个字符串都是走到字符0,这个时候可以直接return 0;如若遇到不同,跳出去之后比较大小,大于就返回1,小于就返回0,或者因为对返回数值的大小没要求,对符号有要求,我们还可以直接返回 *s1 - *s2,代码如下:
在这里插入图片描述

七、strncpy函数的使用

函数原型:char* strcpy(char* destination, const char* source, size_t num);

使用注意事项:
拷贝 num 个字符从源字符串到目标空间
如果源字符串的长度小于 num ,则拷贝完源字符串后,在目标的后面追加0,直到 num 个

请注意,复制过来的时候,不会必带\0
在这里插入图片描述

当源字符串的长度小于 num 的时候,后面会追加0:
在这里插入图片描述

八、strncat函数的使用

函数原型:char* strncat(char* destination, const char* source, size_t num);

使用注意事项:
将 source 指向字符串的前 num 个字符追加到 destination 指向的字符串末尾,再追加一个 \0 字符
在这里插入图片描述

如果 source 指向的字符串的长度小于 num 的时候,只会将字符串中到 \0 的内容追加到 destination 指向的字符串末尾
在这里插入图片描述

九、strncmp函数的使用

函数原型:int strncmp(const char* str1, const char* str2, size_t num);

使用注意事项:
1.比较 str1 和 str2 的前 num 个字符,如果相等就继续往后比较,最多比较 num 个字母,如果提前发现不一样,就提前结束,大的字符所在的字符串大于另外一个,如果 num 个字符都相等,就是相等返回0

十、strstr函数的使用和模拟实现

函数原型:char* strstr(const char* str1, const char* str2);

使用注意事项:
1.函数返回字符串 str2 在字符串 str1 中第一次出现的位置

在这里插入图片描述
输出结果如下:
在这里插入图片描述

首先,我们先创建了一个字符数组,接着通过 strstr 函数找到了str中 ‘‘simple’’ 中 ‘s’ 字符的地址,接着再通过 strncpy 复制过去一个 sample ,接着再输出验证

所以说,strstr 函数的作用是返回字符串在另外一个字符中第一次出现的位置

那么,我们来模拟实现一下 strstr 函数
我们来思考一下实现逻辑:
在这里插入图片描述

1.我们会发现,在str1 和 str2移动的过程中,先打算让str1跑完字符串,一旦遇到 str1 == str2,那么开始向后移动,可是找到还好,万一没找到,都回不去,而且,就算找到了,也不知道起始位置在哪里,所以,我们可能还需要两个指针cur1,cur2
2.于是我们设置两个指针cur1,cur2,继续刚才的思路,先来一个循环 while (*str1 != ‘\0’) ,并且循环里面str1保持更新
3.每次循环开始的时候,都让 cur1 = str1,cur2 = str2,接着判断 *str1 与 *str2是否相等,如果相等的话,开始判断 str2 是否是 str1 的一部分,所以再来个循环,这个循环的条件是什么呢?显然,当 *cur1 与 *cur2 相等的时候,继续循环,一直到不相等或者 *cur2 == ‘\0’ 的时候退出,可是万一 cur1 也走到结尾,那不就要越界访问了吗,于是我们再加个条件 *cur1 != ‘\0’ ,所以这个循环是 while ( *cur1 != ‘\0’ && *cur2 != ‘\0’ && *cur1 == *cur2)
4.等到判断循环结束,我们就来验证,怎么验证,显然只要当上述循环结束的时候,我们验证一下 *cur2 == ‘\0’ 就可以了,若为真,则直接返还 str1
5.若不为真,则我们让 str1 加 1,判断下一个起始字符是否满足题意,至于 cur1 和 cur2,前面说了每次循环开始都会更新的

综上,代码如下:

char* my_strstr(const char* str1, const char* str2)
{if (*str2 == '\0') return (char*)str1; // 如果 str2 是空字符串while (*str1 != '\0'){		const char* cur1 = str1;const char* cur2 = str2;if (*str1 == *str2) {while (*cur1 && *cur2 && *cur1 == *cur2){cur1++;cur2++;}if (*cur2 == '\0') return (char*)str1;}str1++;}return NULL;
}

十一、strtok的使用

函数原型:char* strtok(char* str, const char* sep);

使用注意事项:
1.sep参数指向一个字符串,定义了用做分隔符的字符整合
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
3.strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针,请注意,strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改
4.strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
5.(吊诡)strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
6.如果字符串中不存在更多的标记,则返回NULL指针

在这里插入图片描述

因为第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
在这里插入图片描述在这里插入图片描述

当然也不能一直这么搞,再接下去就是NULL了

这个函数比较特殊,原理大家可自行查找,你可以大胆猜测这个函数内部会有静态变量,因为这样多次进入才确保有上次保留的值

十二、strerror的使用

函数类型:char* strerror(int errnum);

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

在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在errno.h这个头文件中,C语言程序启动的时候就会使用一个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数发生了某种错误,就会将对应的错误码,存放在errno中,而一个错误码的数字是整型很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误码对应的字符串地址返回。

举个例子:

#include <errno.h>  
#include <string.h>  
#include <stdio.h>  // 我们打印一下~10这些错误码对应的信息  
int main()  
{  int i = 0;  for (i = 0; i <= 10; i++) {  printf("%s\n", strerror(i));  }  return 0;  
}

运行结果如下:
在这里插入图片描述
实用的一个场景如下(C语言是可以进行文件操作的,这个我们后面会有详细讲解,在下面这个场景下,我们的打开文件函数fopen是失败的,因此errno会有一个错误码):
在这里插入图片描述
在这里插入图片描述

也可以了解一下 perrorr 函数,perrorr函数相当于一次将上述代码中 if 语句给直接完成了,直接将错误信息打印出来。perrorr函数打印参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。
在这里插入图片描述
它的输出如下:
在这里插入图片描述


总结

  这篇篇幅很长,很多都是相当枯燥的干活,希望你能啃下来~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/422573.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenGL3.3_C++_Windows(37)

调试&#xff1a; 视觉错误与CPU调试不同&#xff0c;在GLSL代码中也不能设置断点&#xff0c;出现错误的时候寻找错误的源头可能会非常困难。 glGetError&#xff08;&#xff09; GLenum glGetError();返回整形数字&#xff0c;查询错误标记&#xff0c;但是当一个错误标记…

C#开发基础之使用四种流行的数据库访问技术ADO.NET、Dapper、EF Core 和 SqlSugar 连接 SQL Server

前言 在这篇文章中&#xff0c;我们将介绍四种流行的数据库访问技术&#xff1a;ADO.NET、Dapper、Entity Framework Core (EF Core) 和 SqlSugar。每种技术都提供了与 SQL Server 进行交互的不同方法&#xff0c;我们将以 TestDB 数据库中的 User 表为例&#xff0c;展示如何…

关于malloc/free的一些知识点

序 关于malloc/free&#xff0c;我们都不陌生&#xff0c;在最开始学习c语言时就相当了解&#xff0c;包括c中的new也是封装的malloc。下边我以glibc实现的malloc来讲述一些关于malloc/free的知识点。 malloc/free malloc和free并不是系统调用&#xff0c;而是运行时库&…

C语言的结构体类型

在我们使用C语言进行编写代码时&#xff0c;常常会使用已经给定的类型来创建变量&#xff0c;比如int型&#xff0c;char型&#xff0c;double型等&#xff0c;而当我们想创建一些较为复杂的东西时&#xff0c;单单用一个类型变量是没办法做到的&#xff0c;比如我们想创建一个…

shader 案例学习笔记之fract函数

fract函数 可以理解为模1取余&#xff0c;获取一个数的小数部分&#xff0c;如果参数是向量&#xff0c;那就是获取每个向量分量上的小数 案例一 #ifdef GL_ES precision mediump float; #endif// 渲染分辨率 uniform vec2 u_resolution; // 程序运行时间 uniform float u_ti…

如何利用 Smarter Balanced 塑造教育领域的 AI 治理

目录 定义挑战 以人为本的设计引领 融入多样性 探索以学生为中心的价值观 探索效果的层次和不同的影响 部位于加利福尼亚州的Smarter Balanced Assessment Consortium 是一个由会员主导的公共组织&#xff0c;为 K-12 和高等教育领域的教育工作者提供评估系统。该组织成立…

初学者指南:MyBatis 入门教程

主要介绍了Mybatis的基本使用、JDBC、数据库连接池、lombok注解&#xff01; 文章目录 前言 什么是Mybatis? 快速入门 使用Mybatis查询所有的用户信息 配置SQL提示 JDBC介绍 Mybatis 数据库连接池 lombok 总结 前言 主要介绍了Mybatis的基本使用、JDBC、数据库连接…

基于stm32单片机使用 RT-Thread 系统的 ADC 外设

一、ADC 介绍 来源&#xff1a;RT-Thread 文档中心   ADC(Analog-to-Digital Converter) 指模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号&#xff0c;例如温度、压力、声音或者图像等&#xff0c;需要转换成更容易储存、处理和发射…

【Linux】:信号与信号产生

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家带来信号和信号的产生相关代码和知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到…

YOLOv10优改系列一:YOLOv10融合C2f_Ghost网络,让YoloV10实现性能的均衡

&#x1f4a5; &#x1f4a5;&#x1f4a5; &#x1f4a5;&#x1f4a5; &#x1f4a5;&#x1f4a5; &#x1f4a5;&#x1f4a5;神经网络专栏改进完整目录&#xff1a;点击 &#x1f497; 只需订阅一个专栏即可享用所有网络改进内容&#xff0c;每周定时更新 文章内容&#x…

微信自动通过好友和自动拉人进群,微加机器人这个功能太好用了

又发现一个好用的功能&#xff0c;之前就想找一个这种工具&#xff0c;现在发现可以利用微加机器人的两个功能来实现&#xff0c;分别是加好友和关键词拉群 首先 微加机器人的专业版 > 功能 > 加好友设置 可以设置一个关键词通过,这样别人加好友的时候只需要输入制定内…

实战案例(5)防火墙通过跨三层MAC识别功能控制三层核心下面的终端

如果网关是在核心设备上面&#xff0c;还能用MAC地址进行控制吗&#xff1f; 办公区域的网段都在三层上面&#xff0c;防火墙还能基于MAC来控制吗&#xff1f; 采用正常配置模式的步骤与思路 &#xff08;1&#xff09;配置思路与上面一样 &#xff08;2&#xff09;与上面区…

通信工程学习:什么是PDF策略决策功能

PDF策略决策功能 PDF策略决策功能&#xff08;Policy Decision Function, PDF&#xff09;在通信网络中&#xff0c;特别是在IP多媒体子系统&#xff08;IMS&#xff09;中&#xff0c;扮演着至关重要的角色。以下是对PDF策略决策功能的详细解释&#xff1a; 一、定义与功能概…

CentOS Stream 8中安装和使用 Docker

docker安装包-CSDN博客 〇、常用的docker命令 docker的作用&#xff1a; 快速进行软件的安装&#xff0c;便于软件环境的维护 docker的镜像: 压缩了指定软件的安装包的文件。使用镜像文件创建容器 docker的容器: 容器可以理解为就是一台小电脑。安装的linux系统&am…

【F的领地】项目拆解:小学教辅资料

项目介绍 虚拟资料项目的赛道其实一直可以做&#xff0c;实际上就是一个信息差项目。 知识付费是常青树&#xff0c;人天生有为知识付费的倾向。 而且虚拟资料通常一份 19~99 的资料有这方面需求很容易就能付款。 主要是产附属的流量再提问题。 我有个同乡&#xff0c;18年…

CircleProgressView 鸿蒙ArkTS自定义View实现圆形进度条

上篇的截图中除了一个上下的箭头&#xff0c;还有一个圆形进度条&#xff0c;今天我们来讲讲这个如何进行实现 我们看这个图形的构造&#xff0c;其实很简单&#xff1a;一个圆形图形&#xff0c;以及一个文本来显示进度 所以我们用一个层叠布局 绘制一个带颜色的圆形&#xff…

Java设计模式—面向对象设计原则(一) ----->开闭原则OCP(完整详解,附有代码+案例)

3.1开闭原则 对扩展开放&#xff0c;对修改关闭。在程序需要进行拓展的时候&#xff0c;不能去修改原有的代码&#xff0c;实现一个热插拔的效果。简言之&#xff0c;是为了使程序的扩展性好&#xff0c;易于维护和升级。想要达到这样的效果&#xff0c;我们需要使用接口和抽象…

怎么选择靠谱AI论文生成工具?看完我的试用都会明白!

2024年上半年开始AI论文写作工具开始火了&#xff0c;层出不穷&#xff01;作为一个经常需要写论文的懒人&#xff0c;我非常好奇这些AI工具的实际效果到底怎么样&#xff1f;为了测试不同工具的实力&#xff0c;我对他们都进行了试用&#xff0c;发现了一些意想不到的结果....…

路由器的固定ip地址是啥意思?固定ip地址有什么好处

‌在当今数字化时代&#xff0c;‌路由器作为连接互联网的重要设备&#xff0c;‌扮演着举足轻重的角色。‌其中&#xff0c;‌路由器的固定IP地址是一个常被提及但可能让人困惑的概念。‌下面跟着虎观代理小二一起将深入探讨路由器的固定IP地址的含义&#xff0c;‌揭示其背后…

QML入门之创建可重用的组件(一)

我们在日常开发中都会封装一些组件以便于项目内重复利用。QML创建可重用组件一般有两种方法。 自定义Item使用Component创建自定义组件 自定义Item 以一个自定义按钮举例&#xff1a; import QtQuick 2.12Rectangle {id: root// 自定义属性property string btnDis: qsTr(&qu…