函数功能简介:
对字符串str进行切割,切割的标志为字符指针q指向的这两个字符 “# *”;。
但是在对这个函数进行调用时,只有在第一次调用时,才会将str这个字符串的首地址传递过去,后面再进行调用时,str的位置放置的都是NULL。
那他是如何实现这个功能的呢????我们可以进行调试分析一下。
第一次调用分析
该函数在对字符串进行切割时,会在目的字符串中查找是否有源字符串包含的字符,如果查找到源字符串中包含的字符,就会将这个字符改为‘\0’。,而且返回刚才传递过来的str首地址。并记录这个位置的地址。
下面我们结合内存视角来观察一下 :
刚开始创建完字符数组str后
执行第一次strtok函数后:
可以看到,原来放置字符 #的位置被修改为字符‘ \0’。其他的没有任何改变。
示意图如下:
第二次调用分析(中间调用该函数时函数的操作方式)
在进行第一次调用结束时,会记录下被修改成字符 '\0'位置的字符的地址。在第二次进行函数调用时,函数的第一个实参为NULL,这里函数会直接从上一次记录的字符的地址的后一个位置开始进行比对,看是不是还有和源字符串相同的字符,如果有,那就将它修改为 字符0,并返回刚才开始比对的字符的地址。
示意图如下:
最后一次调用分析
当字符串被切割完成后(遍历到字符串本身的\0)这个时候再次调用strtok函数,函数会直接返回一个空指针。
有了上面的这些特殊位置的函数调用分析,我们就能自己来实现一个strtok函数
自主实现strtok函数
要注意,该函数有记录功能,所以函数的定义时需要定义一下具有记录功能的变量,在函数结束时依然可以记录数据,所以这里需要定义几个静态区变量。
static int sz1 = NULL;
static int count = NULL;
static char* s1 = NULL;
static char* s2 = NULL;
总的代码实现如下
#include<stdio.h>
#include<assert.h>
#include<string.h>
//模拟实现strtok 字符串切割函数
char* my_strtok(char* str1, const char* str2)
{
assert(str2);
static int sz1 = NULL;
static int count = NULL;
static char* s1 = NULL;
static char* s2 = NULL;
int sz = 0;
if (str1 != NULL)//说明是第一次进入。
{
sz1 = strlen(str1);//计算出str1中所有字符的个数
s2 = str1;//记录初始地址,等下找到分割符时,将这个地址返回。
sz = strlen(str2);
for (*str1; *str1 != 0; str1++)
{
for (int i = 0; i < sz; i++)
{
if (i == 0)
{
count++;
}
if (*str1 == *(str2 + i))
{
*str1 = 0;
s1 = str1;//记录这一次置0的位置。
return s2;
}
}
}
}
else
{
s2 = s1+1;
str1 = s2;
sz = strlen(str2);
for (*str1; *str1 != 0; str1++)
{
for (int i = 0; i < sz; i++)
{
if (i == 0)
{
count++;
}
if (*str1 == *(str2 + i))
{
*str1 = 0;
s1 = str1;//记录这一次置0的位置。
return s2;
}
}
}
if (count > sz1)
{
return NULL;
}
return s2;
}
}
int main()
{
char arr[20] = "12@34.5";
char* p = "@.";
char* str = NULL;
for (str = my_strtok(arr, p); str != NULL; str = my_strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
这里需要注意的就是当目的字符串被遍历完成之后,再次调用该函数会返回一个NULL指针。我是以计数器的方式进行实现,每当成功比对一个字符,就进行一次计数,当计数的个数比目的字符串字符个数大时,说明目的字符串被遍历完成了,此时会返回空指针。
可以看到,实现的结果是符合我们的预期的,如果有更好的算法,可以留言讨论哦!!!!