前言
今天给大家带来一个字符串函数,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函数的使用以及模拟实现的讲解就结束了,如遇到文中错误,可以向我指正。