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

【C语言】strstr函数的使用和模拟

前言

今天给大家带来一个字符串函数,strstr()的使用介绍和模拟实现。

模拟实现这个函数,可以帮助我们更深刻地理解这个函数的功能和提高解决字符串相关问题的能力,有兴趣的话就请往下看吧。

strstr函数介绍

函数功能:

strstr函数的功能是在字符串中找另一个字符串有无出现,或者说就是找一个子字符串。如果能找到,就返回第一次出现的地址,如果找不到就返回空指针。

函数原型:

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

可以看到,我们从函数外部接收两个字符指针,它们各自指向一个字符串的首元素地址,一个字符串是被寻找的字符串,另一个就是要找的子字符串。两个字符指针都用const修饰,因为我们不期望在这个函数中去修改字符串的内容。返回的是一个字符指针,也就是第一次出现的地址或者空指针,同样可以用const进行修饰。

需要的头文件:
#include<string.h>

函数使用:

现在,我们写个代码来看一下这个函数具体是如何使用的:

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "abcdefabcdef";char* p = "efab";char* ret = strstr(arr, p);//创建一个指针接收第一次出现的地址或空指针printf("%s\n", ret);//打印看看结果return 0;
}

在字符串“abcdefabcdef”中寻找“efab”是否出现过,很明显是出现过的,所以这时我们应该返回其第一次出现的位置,也就是字符'e'的位置。当我们用printf("%s\n", ret);将其打印,应该得到的就是“efabcdef”,因为打印会到‘\0’才停下。

vs运行效果:

可以看到,确实是这个效果。那么strstr的使用大致就是这样。

strstr函数的模拟实现

那么,现在我们能否自己写出一个函数来模拟实现库函数strstr的功能呢?当然是可以的,不过要注意将问题分析全面。

分析:

不妨就将我们自己写的strstr函数命名为my_strstr,我们可以先根据strstr的原型,模拟写出my_strstr的原型: 

const char* my_strstr(const char* str1,const char* str2)
{//
}

在上面使用strstr的代码中我们可以看到函数调用部分为char* ret = strstr(arr, p);所以由此我们可以看出应该怎么写相应的my_strstr的原型。

参数部分,因为我们要接受两个字符串,在前者中找后者,因为不期望这两个固定的字符串被修改,所以可以用const进行修饰,而返回值就是如果找到后的第一次出现的地址,也可以用const进行修饰。 

接下来,在开始找之前,我们应该再创建两个字符指针变量并分别用str1和str2对其赋值,也就是复制一对str1、str2,为什么这么做呢?因为当我们移动str1与str2,遇到匹配失败,需要让str2回到起点,str1回到某个位置时,我们已经找不到原来str1和str2指向的位置了,所以我们需要让str1与str2始终指向原本的位置,去移动复制的str1和str2。(等往下说,看到str2回到起点和str1回到某个位置的情况时,可能会更好理解)

假设此时我们的str1和s1指向“abcdefabcdef”(末尾有个隐藏的'\0'),str2和s2指向“cdef”(末尾有个隐藏的),以下是我们这个函数大致的实现逻辑:

首先我们得在str1(简约说法)中找到‘c’,因为如果连‘c’都找不到,那就是直接找不到了。找到‘c’之后就让s1和s2继续往后走进行匹配。

因为s1和s2在这个过程中会向后走,所以当我们最初在s1指向字符串中成功找到‘c’的时候要做一件事情,把当前str1中'c'的位置记录下来,因为这说明从这里开始向后是有可能匹配成功的(也就是说这可能就是我们最终要返回的值),而如果不记录下来最后我们找不回来这个位置。所以我们可以创建一个指针cur进行记录。

然后,假设确实能顺利在str1中找到str2,我们在s1和s2不断向后走的过程中,s2就会遇到‘\0’,这时就意味着匹配成功,我们只需将cur(子字符串第一次出现地址)返回就行了。

复杂情况:

但是往往情况没有那么顺利,我们需要探讨非一次匹配成功的情况,让我们的代码能够应对。

很有可能出现也比较难理解的就是多次匹配的情况

多次匹配是什么意思呢?

现在来举个例子,假设现在我们被找的字符串变为“abbbcdef”(末尾隐藏‘\0’),要找的子字符串变为“bbc”(末尾隐藏‘\0’),那么就会在s2走到‘c’的时候遇到s1不为‘c’的情况,这一次的匹配就失败了。

但是,这时的匹配失败并不代表就是在s1中找不到s2(可以看出来是找得到的),所以我们不能在一次匹配失败后就返回空指针,而是应该让cur往后走一步,让s2回到起点,s1回到cur的位置, 再重新进行匹配:

每次匹配失败我们就将cur向后移动一位,当cur遇到‘\0’的时候,就说明真的找不到了。

还有一种可能就是在某一次s1与s2往后走的时候s1提前遇到了‘\0’,这说明s1的长度已不可能出现完整的str2,可以提前返回空指针。

my_strstr完整代码参考

在分析过各种情况后,我们就能写出strstr函数的模拟实现的代码了:

vs2022运行效果:

到此,strstr函数的使用以及模拟实现的讲解就结束了,如遇到文中错误,可以向我指正。 

相关文章:

【C语言】strstr函数的使用和模拟

前言 今天给大家带来一个字符串函数&#xff0c;strstr()的使用介绍和模拟实现。 模拟实现这个函数&#xff0c;可以帮助我们更深刻地理解这个函数的功能和提高解决字符串相关问题的能力&#xff0c;有兴趣的话就请往下看吧。 strstr函数介绍 函数功能&#xff1a; strstr函…...

五分钟”手撕“异常

目录 一、什么是异常 二、异常的体系和分类 三、异常的处理 1.抛出异常 2.异常的捕获 异常声明throws&#xff1a; try-catch处理 四、finally finally一定会被执行吗&#xff1f; 五、throw和throws区别 六、异常处理的流程 七、自定义异常 一、什么是异常 顾名…...

【vue3+elementuiplus】el-select下拉框会自动触发校验规则

场景&#xff1a;编辑弹框省份字段下拉框必填&#xff0c;触发方式change&#xff0c;有值第一次打开不会触发校验提示&#xff0c;关闭弹框再次打开触发必填校验提示&#xff0c;但是该字段有值 问题的原因是&#xff1a;在关闭弹层事件中&#xff0c;我做了resetfileds&…...

【论文复现】LSTM长短记忆网络

LSTM 前言网络架构总线遗忘门记忆门记忆细胞输出门 模型定义单个LSTM神经元的定义LSTM层内结构的定义 模型训练模型评估代码细节LSTM层单元的首尾的处理配置Tensorflow的GPU版本 前言 LSTM作为经典模型&#xff0c;可以用来做语言模型&#xff0c;实现类似于语言模型的功能&am…...

目标检测YOLO实战应用案例100讲-【自动驾驶】激光雷达

目录 前言 算法原理 测距方法 发射单元 接收单元 扫描单元...

用C语言设计轨道电机的驱动库

一、设计目的 设计能驱动立体轨道电机的抽象驱动程序库。 二、设计要求 命名规范。设计简单&#xff0c;方便使用。体积小。满足电机的移动、停止、初始化、恢复等控制&#xff0c;甚至通过网络控制。 三、设计内容 (一)属性封装 1、定义配置结构体 // 用于配置参数 typed…...

HTML跳动的爱心

目录 写在前面 HTML简介 跳动的爱心 代码分析 运行结果 推荐文章 写在后面 写在前面 哎呀&#xff0c;这是谁的小心心&#xff1f;跳得好快吖&#xff01; HTML简介 老生常谈啦&#xff0c;咱们还是从HTML开始吧&#xff01; HTML是超文本标记语言&#xff08;Hyper…...

汇编原理(二)

寄存器&#xff1a;所有寄存器都是16位&#xff08;0-15&#xff09;&#xff0c;可以存放两个字节 AX,BX,CX,DX存放一般性数据&#xff0c;称为通用寄存器 AX的逻辑结构。最大存放的数据为2的16次方减1。可分为AH和AL&#xff0c;兼容8位寄存器。 字&#xff1a;1word 2Byte…...

Android Studio开发之路(十三)主题影响Button颜色问题解决及button自定义样式

一、问题描述 在开发过程中发现安卓的默认主题色是紫色&#xff0c;并且会导致button也是紫色&#xff0c;有时直接在xml布局文件中直接设置button的背景色或者设置背景图片不起效果 方案一、如果是app&#xff0c;可以直接设置主题颜色 比如&#xff0c;将主题设置为白色&a…...

eNSP学习——OSPF单区域配置

目录 相关命令 实验背景 实验目的 实验步骤 实验拓扑 实验编址 实验步骤 1、基础配置 2、部署单区域OSPF网络 3、检查OSPF单区域的配置结果 OSPF——开放式最短路径优先 基于链路状态的协议&#xff0c;具有收敛快、路由无环、扩展性好等优点&#xff1b; 相关命令 […...

深度学习中的优化算法二(Pytorch 19)

一 梯度下降 尽管梯度下降&#xff08;gradient descent&#xff09;很少直接用于深度学习&#xff0c;但了解它是理解下一节 随机梯度下降算法 的关键。例如&#xff0c;由于学习率过大&#xff0c;优化问题可能会发散&#xff0c;这种现象早已在梯度下降中出现。同样地&…...

R实验 方差分析

实验目的&#xff1a; 掌握单因素方差分析的思想和方法&#xff1b; 掌握多重均值检验方法&#xff1b; 掌握多个总体的方差齐性检验&#xff1b; 掌握Kruskal-Wallis秩和检验的思想和方法&#xff1b; 掌握多重Wilcoxon秩和检验的思想和方法。 实验内容&#xff1a; &…...

AI智能体|手把手教你使用扣子Coze图像流的文生图功能

大家好&#xff0c;我是无界生长。 AI智能体&#xff5c;手把手教你使用扣子Coze图像流的文生图功能本文详细介绍了Coze平台的\x26quot;图像流\x26quot;功能中的\x26quot;文生图\x26quot;节点&#xff0c;包括创建图像流、编排文生图节点、节点参数配置&#xff0c;并通过案例…...

应用程序图标提取

文章目录 [toc]提取过程提取案例——提取7-zip应用程序的图标 提取过程 找到需要提取图标的应用程序的.exe文件 复制.exe文件到桌面&#xff0c;并将复制的.exe文件后缀改为.zip 使用解压工具7-zip解压.zip文件 在解压后的文件夹中&#xff0c;在.rsrc/ICON路径下的.ico文件…...

Excel表格在线解密:轻松解密密码,快速恢复数据

忘记了excel表格密码&#xff1f;教你简单两步走&#xff1a;具体步骤如下。首先&#xff0c;在百度搜索中键入“密码帝官网”。其次&#xff0c;点击“立即开始”&#xff0c;在用户中心上传表格文件即可找回密码。这种方法不用下载软件&#xff0c;操作简单易行&#xff0c;适…...

springboot小结1

什么是springboot ​ Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的&#xff0c;使用它可以做到专注于Spring应用的开发&#xff0c;而无需过多关注XML的配置。 ​ 简单来说&#xff0c;它提供了一堆依赖打包Starter&#xff0c;并已经按照使用习惯解决…...

【Qt 学习笔记】Qt窗口 | 菜单栏 | QMenuBar的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 菜单栏 | QMenuBar的使用及说明 文章编号&#xff1a;Qt 学习…...

Spark运行模式详解

Spark概述 Spark 可以在多种不同的运行模式下执行&#xff0c;每种模式都有其自身的特点和适用场景。 部署Spark集群大体上分为两种模式&#xff1a;单机模式与集群模式。大多数分布式框架都支持单机模式&#xff0c;方便开发者调试框架的运行环境。但是在生产环境中&#xff…...

vcpkg环境配置

vcpkg 使用linux相关库&#xff0c;设置环境变量VCPKG_ROOT&#xff0c;设置cmake工具链$VCPKG_ROOT/scripts\buildsystems\vcpkg.cmake set VCPKG_DEFAULT_TRIPLETx64-windows .\vcpkg.exe install fftw3 freetype gettext glibmm gtkmm libjpeg-turbo libpng libxmlpp libs…...

python学习:基础语句

目录 条件语句 循环语句 for 循环 while 循环 break continue 条件语句 Python提供了 if、elif、else 来进行逻辑判断。格式如下&#xff1a; Pythonif 判断条件1: 执行语句1... elif 判断条件2: 执行语句2... elif 判断条件3: 执行语句3... else: 执行语句4…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...