C字符串和内存函数介绍(三)——其他的字符串函数

在#include<string.h>的这个头文件里面,除了前面给大家介绍的两大类——长度固定的字符串函数长度不固定的字符串函数。还有一些函数以其独特的用途占据一席之地。

今天要给大家介绍的是下面这三个字符串函数:strstr,strtok,strerror

学习指南:对于strstr函数有能力的小伙伴可以掌握一下它的模拟实现,但是strtok函数和strerror函数只需要知道它们的底层原理和基本使用即可。

———————————————————————————————————————————

附(难度评析表):

学习难度:

strstr:  ⭐⭐⭐⭐⭐

strtok:     ⭐⭐⭐

strerror:   ⭐

理解难度:

strstr:    ⭐ ⭐ ⭐(不考虑Kmp的部分,加上Kmp它完全可以达到五颗星的理解难度)

strtok: ⭐ ⭐ ⭐ ⭐

strerror: ⭐

目录

一、strstr函数:

1. 介绍:

2. 模拟实现:

二、strtok函数:

1. 介绍:

2. strtok函数的应用场景与工作原理:

应用场景:

工作原理:

(第一种情况)——str参数不为NULL指针:

(第二种情况)——str参数为NULL指针:

三、strerror函数:

1. 介绍:

2. 关于错误码:

3. perror函数的使用:


一、strstr函数:

1. 介绍:

函数原型:

const char * strstr ( const char * str1, const char * str2 );
函数名功能描述头文件
strstr在str1字符串查找str2字符串#include<string.h>

返回值说明:返回一个指针,该指针指向了str2在str1中首次出现的位置。如果str2不在str1里面,则返回一个NULL指针。

2. 模拟实现:

思路解析:

养成编程的好习惯:我们让str1和str2始终指向各自的字符串的首元素地址不动它们,这样一来可以避免在之后的查找遍历过程中,因为找不到str1和str2首元素的地址而无法回溯的问题。然后分别定义两个指针s1,s2遍历str1数组和str2数组。

如果对应位置两个字符串的值相等(即:s1 == s2),我们就让s1和s2同时往下走(s1++,s2++)。反之让s1回到最开始查找的位置的下一个位置(该位置我们可以用一个cp指针进行标记)。然后s2回到str2的位置(即子串的第一个元素的位置)。

然后重复上面的步骤,大概思路就是这样的。另外的话考虑到如果str1字符串剩余待检索字符不足str2的实际长度,那也就没必要继续检索了,所以在这之前我们可以先求一下str2数组的长度(这个过程可以用strlen函数,也可以自己写代码)。详情请见代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
//模拟实现strstr函数的功能:(暴力求解)const char* my_strstr(const char* str1, const char* str2)
{const char* cp = str1;//记录开始遍历时的位置const char* s1 = str1;//遍历str1的指针const char* s2 = str2;//遍历str2的指针int cnt = 0;while (*s2++){cnt++;}while (*(cp+cnt-1)){s1 = cp;s2 = str2;while (*s2 && *s1 && *s1 == *s2){s1++;s2++;}if (*s2 != '\0'){cp++;}else{return cp;}}return NULL;
}int main()
{const char* str1 = "abbbcef";const char* str2 = "bc";const char* ret = my_strstr(str1, str2);if (ret){printf("%s", ret);}else{printf("str1里面找不着str2");}return 0;
}

上面的代码实际上一种暴力求解的思路,当然也有地方把这也叫作BF算法,这个是解决这种问题的通法。但是不是最高效的算法,这个在一个字符里面找另一个字符普遍认为最高效是KMP算法。这个算法这里博主也不过多介绍了。但是直接给代码很多小伙伴是很难理解的,而且也不是靠言语一两句话就可以讲明白的。

所以感兴趣的小伙伴可以自己下去了解一下,这里推介一个宝藏UP主的KMP算法课程,UP主很有耐心给大家介绍了该算法,虽然时间有点长,但是我觉得很适合小白来学习这个算法。

———————————————————————————————————————————

附(高质量KMP算法讲解):

【完整版】终于有人讲清楚了KMP算法,Java语言C语言实现_哔哩哔哩_bilibili

二、strtok函数:

1. 介绍:

函数原型:

char * strtok ( char * str, const char * sep );
函数名功能描述头文件
strtok按照指定分隔符对字符串进行切割#include<string.h>

参数说明:

(一)第一个参数str是待分割字符数组的首元素的地址,另外值得一提的是strtok函数会修改str的内容,所以要求str的内容必须是可以被修改的。如果str作为字符指针本身其被const关键字所修饰,亦或者是保存常量字符串首元素地址的指针,那这种传参将不被允许,是一种错误的传参方式。如图所示:

但是当你的源文件以.c作为后缀名的时候,这个传参将会被允许,但也只是表面上的允许,实际运行时会出错。这是由于编译器对于.c为后缀的源文件编译检查不够严格导致的。

(二)第二个参数sep是分隔符的集合,strtok函数允许分隔符有多个。另外sep可以是一个const char*类型的字符指针,也可以是一个字符数组的首元素的地址。

注:怎么理解上面的传参关系:我觉得可以用下面这一句话来总结概括:你可以让有大能力的人做小事,但是你不能让有小能力的或者几乎没有能力的人去做大事。

2. strtok函数的应用场景与工作原理:

应用场景:

strtok函数是用来进行字符串切割的,通过这个函数我们可以对诸如电子邮箱的信息进行一个切割提取,就比如对于一个电子邮箱:zhangsan@year.net,如果我希望能够提取到zhangsan,year,net这样的有用信息的话就可以用这样的函数。

再还有就是在计算机网路里面,对于诸如主机ID——192.168.101.32(底层使用点分十进制方式对无符号整数进行处理的结果)这样的进行一个信息提取我们也能用strtok函数。

再其次就是在密码学里面,对于很长很长的密码报文,我们可以定义一些特别的分割符来对密码报文进行一个切片的处理,以达到化繁为简的目的……

工作原理:

(第一种情况)——str参数不为NULL指针:

strtok函数处理的对象主要是str,也就是第一个参数,如果第一个参数str不是NULL指针,它会根据str的内容,同时对照sep分割符集合里面的分隔符的种类。在发现首个分隔符之后,将其置为'\0',并且记下该分隔符在数组中的位置,做一个标记,为下一轮的分割字符串做准备(后面会证明这一点)。是的,你没听错,这个函数具有记忆功能!!!

之后该函数会返回该标记开始时的位置。注意这里说的是标记开始时的位置,而并非标记本身位置。eg:对于字符串:zhangsan@year.net,如果我们的分隔符sep有“@”和“.”,那对于分隔符“@”而言,它开始时的位置应该是字母z所在的位置。而对于分隔符“.”而言,它开始时的位置应该是字母y所在的位置。

我们这里就以上面的字符串zhannsan@year.net为例,来使用strtok函数对它进行第一次分割,如图所示:

但是也有可能str里面本身就没有sep分隔符里面的任何一个分隔符,那么这个函数将返回str本身。同时标记数组末尾,以表示分割已完成。后面继续分割将返回空指针。

至于如何进行下一轮的分割我们在下面会讲,这里先给大家演示一下str数组里面不存在sep分隔符的话会出现的情况。如图所示:

注:这里的sep数组里面保存的只有空格和"\0"。

(第二种情况)——str参数为NULL指针:

如果第一个参数str用户传过去的是空指针的话,那strtok函数将会从上一次标记的位置开始,去寻找新的分割符。将其置为'\0'并标记该位置。通过这种方式来实现对同一个字符串的第二轮分割。

还是以str = "zhangsan@year.net",sep = "@."为例,我们在进行了第一轮分割的基础之上,进行第二次分割和第三次分割应该这样去传参和使用。如图所示:

之前我们有提到说:该函数具有记忆功能,在这里就得到了很好的一个体现。第一次分割结束之后就记下了@在数组中的位置,第二次分割就从该位置开始往下找新标记,找到.,然后记下.在数组中位置。依次类推,最后会标记在数组末尾,以表示分割已完成。

再继续进行分割则会返回空指针,那是不是这样的呢,我们也可以来测试一下:

大概就是这么一回事,那有小伙伴们可能又要问了,那如果在此之前strtok函数没有标记位置呢。换句话说,strtok函数没有进行哪怕任何一次的有效分割,一上来str参数就传个NULL指针过去会怎么样。这里直接说结论:这个时候会导致程序的崩溃!!!所以在实际使用过程中应当避免这样的行为。

三、strerror函数:

1. 介绍:

函数原型:

char * strerror ( int errnum );
函数名功能描述头文件
strerror打印错误码所对应的错误信息#include<string.h>

返回值说明:返回错误信息字符串的起始地址。

2. 关于错误码:

首先错误码是一个整数,不同的错误码标识程序在运行过程中不同的错误信息。错误码有很多,大概100来条左右。我们可以通过下面这个代码来看一下前十个错误码(0~9)所代表的含义。

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

运行截图:

在实际开发过程中,如果仅仅是用strerror函数来查看错误码所对应的错误信息,这是没有什么实际意义的。实际上每一个程序都有一个全局变量——errno。这个全局变量是保存在头文件#include<string.h>里面(更准确一点来说是在头文件errno.h里面)。保存了当前程序错误码的信息。

当程序状态异常时,编译器会根据当前状态自动更新errno的值,这个全局变量联合strerrno函数来使用可以帮助开发人员更快更准确地确定程序的问题所在。

下面是一个使用示范:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>int main()
{//打开文件:FILE* fin = fopen("Unexist.txt", "r");if (fin == NULL){printf("发生错误:%s", strerror(errno));return errno;}//读文件://......//关闭文件:fclose(fin);fin = NULL;return 0;
}

运行截图:

注:上面的代码阅读需要一定的C语言文件操作的基本知识。C语言打开文件的函数是fopen,打开文件的方式则有r,w,a三种方式,分别表示只读,只写和追加。如果用户以只写和追加的方式来打开文件,如果当前文件不存在系统会自动创建文件。而当用户用只读的方式打开文件时,如果文件不存在则会出现错误。

3. perror函数的使用:

提到程序错误信息的打印,就不得不提及一下perror函数了。perror函数是stdio.h标准输入输出流头文件里面的一个标准库函数

函数原型如下所示:

void perror ( const char * str )

该函数的功能是向屏幕打印错误信息,该函数的使用不需要用户对errno这个全局变量有所了解。他会自己检索底层的错误信息后并告诉我们的用户。

除此之外,函数的参数是用户输入的字符常量,这是用户希望打印的自定义信息。之后perror函数向显示器设备打印信息将按照如下格式进行:自定义信息:错误信息这里值得一提的是打印格式中的“冒号”是编译器自己会带上的,不需要用户主动添加!

我们下面使用perror函数来改造上面的代码,以实现和使用strerror函数一样的效果:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>int main()
{//打开文件:FILE* fin = fopen("date.txt", "r");if (fin == NULL){perror("发现错误");return errno;}//读文件://......//关闭文件:fclose(fin);fin = NULL;return 0;
}

运行截图:

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

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

相关文章

前端web性能统计

前端web性能统计 1. 背景2. 业界方案2.1 腾讯2.2 蚂蚁金服2.3 字节跳动2.4 美团 3. 相关观念3.1 RAIL模型3.2 性能指标3.3 真实用户监控3.4 performance 4. 性能监控工具介绍5. 推荐采用方案 1. 背景 在如今的数字时代&#xff0c;网站和应用程序的性能对用户体验至关重要。用…

STM32MP135裸机编程:唯一ID(UID)、设备标识号、设备版本

0 资料准备 1.STM32MP13xx参考手册1 唯一ID&#xff08;UID&#xff09;、设备标识号、设备版本 1.1 寄存器说明 &#xff08;1&#xff09;唯一ID 唯一ID可以用于生成USB序列号或者为其它应用所使用&#xff08;例如程序加密&#xff09;。 &#xff08;2&#xff09;设备…

Git代码管理工具 — 3 Git基本操作指令详解

目录 1 获取本地仓库 2 基础操作指令 2.1 基础操作指令框架 2.2 git status查看修改的状态 2.3 git add添加工作区到暂存区 2.4 提交暂存区到本地仓库 2.5 git log查看提交日志 2.6 git reflog查看已经删除的记录 2.7 git reset版本回退 2.8 添加文件至忽略列表 1 获…

0.单片机工作原理

文章目录 最小系统 单片机芯片 时钟电路 复位电路 电源 最小系统 单片机芯片 本次51单片机的芯片为&#xff1a;STC89C52 Flash(闪存)程序存储器&#xff1a;存储程序的空间 SRAM&#xff1a;数据存储器&#xff0c;可用于存放程序执行的中间结果和过程数据 DPTR&#xff1a;…

2024-07-14 Unity插件 Odin Inspector2 —— Essential Attributes

文章目录 1 说明2 重要特性2.1 AssetsOnly / SceneObjectsOnly2.2 CustomValueDrawer2.3 OnValueChanged2.4 DetailedInfoBox2.5 EnableGUI2.6 GUIColor2.7 HideLabel2.8 PropertyOrder2.9 PropertySpace2.10 ReadOnly2.11 Required2.12 RequiredIn&#xff08;*&#xff09;2.…

机器人相关工科专业课程体系

机器人相关工科专业课程体系 前言传统工科专业机械工程自动化/控制工程计算机科学与技术 新兴工科专业智能制造人工智能机器人工程 总结Reference: 前言 机器人工程专业是一个多领域交叉的前沿学科&#xff0c;涉及自然科学、工程技术、社会科学、人文科学等相关学科的理论、方…

Linux编程(三)—makefile快速编译

起因 linux环境下&#xff0c;编译c程序很麻烦&#xff0c;后面g -o demo demo.cpp ……往往跟了许多许多东西&#xff0c;这些每次编译的时候都要书写&#xff0c;所以就产生了makefile快速编译方式&#xff0c;具体操作如下。 怎么用makefile? 第一步&#xff1a;下载 m…

WPF学习(2) -- 样式基础

一、代码 <Window x:Class"学习.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/blend/2008&…

MySQL 聚簇索引和非聚簇索引有什么区别?

聚簇索引&#xff08;主键索引&#xff09;、非聚簇索引&#xff08;二级索引&#xff09;。 这两者之间的最主要的区别是 B 树的叶子节点存放的内容不同&#xff1a; 聚簇索引的 B 树叶子节点存放的是主键值完整的记录&#xff1b;非聚簇索引的 B 树叶子节点存放的是索引值主…

【面试八股总结】C++内存管理:内存分区、内存泄漏、new和delete、malloc和free

参考资料&#xff1a;代码随想录、阿秀 一、内存分区 &#xff08;1&#xff09;栈区 在执行函数时&#xff0c;函数内部局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很高&am…

Postman下载及使用说明

Postman使用说明 Postman是什么&#xff1f; ​ Postman是一款接口对接工具【接口测试工具】 接口&#xff08;前端接口&#xff09;是什么&#xff1f; ​ 前端发送的请求普遍被称为接口 ​ 通常有网页的uri参数格式json/key-value请求方式post/get响应请求的格式json 接…

关闭Ubuntu烦人的apport

先来看让人绷不住的&#xff08;恼&#xff09; 我查半天apport是啥玩意发现就一错误报告弹窗&#xff0c;十秒钟给我弹一次一天给我内存弹爆了 就算我程序就算真的不停崩溃&#xff0c;也没你这傻比apport杀伤性强啊&#xff1f;&#xff1f;&#xff1f; 原则上是不建议关闭…

牛客周赛 Round 51 解题报告 | 珂学家

前言 题解 典题场&#xff0c; EF都有很多种解法 A. 小红的同余 性质: 相邻两数互质 x ( m 1 ) / 2 x (m1)/2 x(m1)/2 m int(input())print ((m 1) // 2)B. 小红的三倍数 性质: 各个位数之和是3的倍数&#xff0c;可被3整除 和数的组合顺序无关 n int(input()) arr…

MySQL高级面试点

Explain语句结果中各个字段分别代表什么 id&#xff1a;查询语句没出现一个select关键字&#xff0c;MySQL就会给他分配一个唯一id select_type&#xff1a; select关键字对应哪个查询的类型 simple&#xff1a;简单的查询 不包含任何子查询 primary&#xff1a;查询中如果…

网络安全设备——EDR

网络安全中的EDR&#xff08;Endpoint Detection and Response&#xff0c;端点检测与响应&#xff09;是一种主动式的端点安全解决方案&#xff0c;它专注于监控、检测和响应计算机和终端设备上的安全威胁。以下是EDR的详细解释&#xff1a; 一、定义与功能 EDR是一种网络安…

repo sync同步出错解决

当出现下面提示时 e list of known hosts. Fetching: 100% (1167/1167), done in 44.619s info: A new version of repo is available warning: repo is not tracking a remote branch, so it will not receive updates Repo command failed: RepoUnhandledExceptionError …

Zabbix6.0使用自带模板(Redis by Zabbix agent 2)监控Redis数据库

注意&#xff1a;Zabbix6.0使用Redis by Zabbix agent 2 模板可直接监控Redis数据。 1、添加Redis账号密码信息(如果Redis没有设置密码可省略此步骤) vim zabbix_agent2.confPlugins.Redis.Sessions.redis.Uritcp://redis.huayunworld.com:6379 Plugins.Redis.Sessions.redis…

025-GeoGebra中级篇-曲线(1)_显式曲线、隐式曲线、参数曲线

写到曲线这一章节&#xff0c;不得不先梳理一下数学中关于曲线这一部分的内容&#xff0c;我们常见的曲线有显式曲线、隐式曲线、参数曲线&#xff0c;当然还有极坐标曲线、参数化曲面、分段函数曲线、分形曲线、复数平面上的曲线、随机曲线、和非线性动力系统的轨迹&#xff0…

在 PostgreSQL 里如何实现数据的分布式事务的回滚和补偿机制?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何实现数据的分布式事务的回滚和补偿机制一、分布式事务的概念与挑战&#xff08;一…

STM32智能农田监测系统教程

目录 引言环境准备智能农田监测系统基础代码实现&#xff1a;实现智能农田监测系统 4.1 数据采集模块 4.2 数据处理与分析模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;农田监测与管理问题解决方案与优化收尾与总结 1. 引言 智能农田监测系统通…