看到了这篇文章,不怕你学不会字符串函数!!!!!

讲解字符串函数和内存函数原理及模拟实现

  • 字符串函数
    • 1.strlen
      • 一.农民朴素数数版
      • 二.花里胡哨递归法
      • 三.知识就是力量法
    • 2.strcpy
    • 3.strcat
    • 4.strcmp
    • 5.strstr
    • 6.strtok
    • 7.strerror

敲下这一行的时候是十月七号上午九点二十八分,兄弟们可不敢以为阿涛这一个国庆节在哪里胡吃海喝啊!阿涛基本上就是在宿舍度过的,这不,抓住国庆节的尾巴,给兄弟们更新几篇高质量博客嘛!
希望大家国庆都快乐!!

字符串函数

1.strlen

strlen函数是老熟人了,之前写sizeof的时候我就提起过它,后面应该也或多或少涉及过。
在这里插入图片描述
strlen函数的参数是一个char*的指针,返回类型是size_t,那这个size_t又是何方神圣呢?我们转到vs2019来一看究竟:
这里我们在编译器里面打出:size_t然后转到定义就可以看到如下字样!在这里插入图片描述
不难看出,size_t实际上就是unsigned int 的一个类型重命名,所以strlen返回的就是一个无符号整数,其实这是很好理解的,不可能说一个字符串长度是负数吧!
那么关于strlen我们还可以再讲一点:
我们传给strlen的是一个指针,那么我们顺着这个指针往后找,一直到找到了\0为止。其中我们计算的数字就是从头到\0这之间的字符数,\0不算在内!
关于strlen的模拟实现,我们现在至少可以写出三种写法,有兄弟现在肯定是一脸懵逼,不要慌,我们写的一定是我们学过的知识!

一.农民朴素数数版

既然我们这里是模拟实现,那么函数的参数,返回类型就应该和原版保持一致,这才叫高仿,这就叫做专业!

size_t mystrlen(const char* str)//这里我们只是计数,没必要改变字符串内容,
//而且这里是常量字符串“abcdef”,放在可读区里面,你想改还改不了,所以我们这里用const修饰
//*str,主打的就是一个严谨,主打的就是一个专业!

{
	assert(str);//由于后面我们要对str进行解引用操作,而我们一旦对空指针进行解引用,就有可能让程序崩溃,在下面我再给兄弟们试上一试!!
	int count = 0;
	while (*str)
	{
		str++;
		count++;
	}
	return count;
}
int main()
{
	//模拟实现strlen
	//1.农民朴素版
	char* p = "abcdef";//从这里我们可以回想到之前我们说过的,C语言里面是没有字符串变量的
	//我们存放的也从来不是一整个字符串,而是把字符串首字符的地址取出来,放在字符指针变量里面
	printf("%d", mystrlen(p));//链式访问,就是把一个函数的返回值作为另一个函数的参数
	//printf函数有两个参数一个是%d另一个就是,后面的内容!
	
	return 0;
}

在这里插入图片描述
从结果上面来看,我们可以说是大获全胜!
下面阿涛用自己的电脑给兄弟们显示一篇解引用空字符串会发生些什么!!!!!
看到了这篇文章,不怕你学不会字符串函数!!!!!_第1张图片
这里是给了一个警告然后就自动退出了程序!!!!
所以日后我们在对指针解引用时最好能够判断一下它是不是空指针!!

二.花里胡哨递归法

不知道兄弟们是否还记得,在遥远的从前,阿涛曾经专门写过一篇文章讲过一些关于递归的知识,这里我们活学活用一波(没看过之前的文章的兄弟们可以去补一补!!)

size_t mystrlen2(const char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
		return 1 + mystrlen2(str + 1);//递归就是把一堆相同的大操作化简为一个个小操作
}

int main()
{
	char* p = "abcdef";
	assert(p);//刚才没给兄弟们讲清楚,使用这个库函数要引用头文件#include
	printf("%d",mystrlen2(p));
	return 0;

在这里插入图片描述

三.知识就是力量法

好像就是上一篇博客吧,我给兄弟们讲解了关于指针的知识,应该差不多在结尾的时候,我告诉了兄弟们,指针减去指针所得到的就是指针之间的元素个数,那么今天我们就用学到的知识,活学活用一波!!

size_t mystrlen3(const char* str)
{
	char* end = str;
	while (*end != '\0')//这里不可以写成“\0”刚才我就是写错了,看了大半天还没有看出来这里写错了……
	{
		end++;
	}
	return end - str;
}

int main()
{
	char* p = "abcdef";
	assert(p);
	printf("%d", mystrlen3(p));
	return 0;

}

在这里插入图片描述

2.strcpy

看到了这篇文章,不怕你学不会字符串函数!!!!!_第2张图片
strcpy函数可能大家对他没有像第一个库函数那么熟悉,但也不至于说是陌生的很,之前我们应该也是稍有涉及的,那么下面我们来仔细看一下这个库函数!
cpy给人的感觉是不是和copy高度相似呢?是的!从名字上面我们也是可以猜到这个函数的意义应该就是在于拷贝了!
这个函数有两个参数,destination就是目的地的意思,source是来源地,那么我们不妨大胆一点,我就认为这个函数的实现原理就是,把字符串从来源地一直拷贝到目的地!
大致上确实是这样的,但是其中还有几个细节需要稍加注意:
1.从参数上来看无论是源头地还是目的地,我们都是把它的地址传给函数,也就是把首元素的地址传给函数!
2.我们函数的返回类型竟然是char 的,也就是说我们给函数传了两个地址过去,到头来函数也是会给我们返回一个地址的,那么这个地址不可能还是源头地的地址吧,那样做没有意义的,所以我们返回的肯定是拷贝好了之后的目的地的首元素地址啊!
3.我们前半段讲解的都叫做字符串函数,意思就是他们的作用范围仅限于字符串,你没有办法通过 strcpy把一个整型数组的内容拷贝到另一个整形数组里面,这就叫做规矩!
4.我们拷贝内容总归要有个头吧,总不能一直这么拷贝下去吧,生产队的骡子都不敢这样用的!我们之前又说到过,凡是字符串都会在字符串的末尾自动偷偷地给你加上一个
**\0***,这个***\0***你是看不到的,但是它又是确切存在的,同时为了保持和原字符串一个样子,这个\0我们还必须给它拷过去!
以上大概就是我们在模拟实现 strcpy需要注意的重点,至于细节我们在代码直接下手!

char* mystrcpy(char* a, const char* b)
{
	assert(*a &*b);
	char* str = a;
	while (*a++ = *b++)
	{
		;
	}
	return str;
}
int main()
{
	char a[] = "\0";
	char b[] = "abcdef";
	printf(mystrcpy(a,b));
	return 0;
}

我们先不加以讲解,先运行看看:
看到了这篇文章,不怕你学不会字符串函数!!!!!_第3张图片
好家伙,直接一个大八叉打到你的脸上,那这究竟是什么情况呢?
经过我们不懈的努力,不断地调试,我们终于终于发现了问题所在:
在这里插入图片描述
这种不在数组括号里面加入具体数字的做法是有风险的,这个数组的大小完全就会根据后面地内存自动填充,就比如第一个数组内存其实就开辟了一个字节的大小用来存放这个\0;而第二个数组内存却足足有着七个元素,这时候有没有兄弟急得蹦了起来?明明就是六个元素啊,你为什么要说七个?指鹿为马?之前我们不是说过了吗,字符串会自动在最后加上一个\0来作为结束标志,6+1就是7喽!
那我们刚才操作就是把一个七个字符的字符串强行塞进只有一个自己的空间里面,当然塞不下啦!所以我们在设计的时候就要确保我们的目的地有足够大的空间来接收源头的!

char* mystrcpy(char* a, const char* b)//我们是没有理由对源头的内容进行更改的,更别提源头的
//元素是字符了,根本改不了,加上const进行保护!
{
	assert(a && b);//我们这里是不能对于空指针进行解引用,所以只要对地址进行判读就可以了!
	//&&两个&,少一个意思就完全不一样了!
	char* str = a;//基于我们最后返回的还要是目的地首元素的地址,我们事先保存一份,到时候直接返回
	while (*a++ = *b++)//我当时看到这行代码的时候都不由得惊叹,这简直就是神来之笔
		//明明使用的都是最基本不过的知识,但是却让人回味无穷!
		//首先++优先级没有*来的高,我们首先是赋值操作,然后源头地和目的地地址各自加一
		//找到了下一个元素的地址,再进行赋值。如此循环
		//这样做的好处就是先使用后加加,可以直接把我们最后一个‘\0’赋值过来
		//而且这个表达式的值就是赋值之后的*a,当它接收到\0的时候循环停止!
	{
		;//这里的意思就是这个循环我不需要他做任何事情!
	}
	return str;//返回
}
int main()
{
	char a[10] = "\0";//加上了这个10我们才算开辟了一个足够大的空间
	char b[] = "abcdef";//表面上六个字母六个元素,其实还有一个隐藏的'\0'
	printf(mystrcpy(a,b));
	return 0;
}

看到了这篇文章,不怕你学不会字符串函数!!!!!_第4张图片
我们可以看到最后的效果异常的好!
最后我们再对最后的打印代码专门进行一定讲解!

printf(mystrcpy(a,b));

这是不是和我们之前的打印函数不太一样?其实我们细想一下:

	printf("abcdef");

这样的代码执行效果是什么?是不是也就是打印出了这个字符串啊?
我们之气不止一次讲过,C语言是没有字符串变量的,我们所打出的字符串本质上都是存放的字符串首字符的地址,把这个地址存放到一个地方,然后我们要使用这个字符串的话,程序基本上都是会往后找,一直找到我们那个隐藏的’\0’为止这样子是不是也是达到了存放字符串的效果?
一样的道理,我们返回的不就是首字符的地址嘛?是不是和上面的代码其实传给printf函数的是一个东西呢?

3.strcat

在这里插入图片描述
我们可以看到strcat就函数参数和返回类型来说是不是和strcpy高度相似呢?其实我感觉这些个字符串函数就像是表兄弟一样,626,624,627这几个相貌差别其实不大,但是它们的能力确实天差地别的!
话说回来strcat函数的功能其实就是追加字符串,总体来说就是把源头地的字符串给增加或者用一个比较高深的词“嫁接”到目的地字符串后面,这样他们是不是就形成了一个全新的字符串?
这样又涉及到了‘\0’这位老哥了,按照道理我们需要打印出来的是两串字符串拼接而成的,那么原先目的地字符串末尾的那个“\0”就留他不得,我们就需要通过源头地第一个字符把这个万恶的***“\0”***给覆盖掉!
其实原理也不是很复杂!

char* mystrcat(char* a, char* b)
{
	assert(a&&b);//断言一下子
	char* str = a;
	while (*a)//为什么不用之间讲过的高级代码呢?
	{
		a++;
	}
	while (*a++ = *b++);//这里为什么直接;了?没有问题吗?
	return str;
}

int main()
{
	char a[20] = "hello ";//故意设置大一点
	char b[] = { "world!" };
	printf(mystrcat(a,b));//接受的是首字符地址,一直打印知道\0为止!
	return 0;
}

可以看到啊,这里我给兄弟们提出了两个问题:
1.为啥在第一个循环语句里面我们循规蹈矩了?
2.为啥这里while语句后面直接跟了;?

	while (*a)//为什么不用之间讲过的高级代码呢?
	{
		a++;
	}
	while (*a++)
	{
		;
	}

无非不过就是这两种形式的比较呗!第一种形式比较扎实,我们不可能找到它一点点问题,第二种就比较考验水平了,那么我们想一下,加入此时此刻已经移动到了“\0”的位置,按照道理我们的a是不是就应该停了下来,继续往后面走的话,后面的字符串就不能够覆盖到这个“\0”了!但是此时我们这个++是后置++,使用完了才会起作用,也必须要起作用!谁管你有没有停止循环?所以此时a又往后跳了一格,我们如果想要正常使用,还必须再来个a- -,所以说就我感觉,我还是比较倾向于第一种!
再来第二个问题:
按照道理我们循环语句后面是要跟着一个执行语句的,如果执行语句超过了一条,我们还要用{ }给他括起来,这就是代码块!
但是这里直接就来了个;是几个意思?
其实这也不难理解,不用{ }说明这个循环语句的执行语句只有一个呗,是不是也就相当于这样:

	while (*a++ = *b++)
		;

是不是也就是这样子:

	while (*a++ = *b++)
	{
			;
	}

这一下子是不是就是豁然开朗了?
我们来运行一下证明一下我们是正确的!
看到了这篇文章,不怕你学不会字符串函数!!!!!_第5张图片

4.strcmp

这个strcmp表面上和strcpy好像很像,但是我们可千万不能搞混啊!
strcmp的用处就是比较两个字符串,好了我们再次进入会议模式:
之前我们讲过字符串之间的比较是不能像比较两个整形那样子直接用> <进行的,而应该使用strcmp函数:
看到了这篇文章,不怕你学不会字符串函数!!!!!_第6张图片
这次有所不同了,这次函数返回类型是int类型了!
看到了这篇文章,不怕你学不会字符串函数!!!!!_第7张图片
这里有一堆英文,大概意思就是如果前面的字符串大于后面的字符串那么函数返回的就是一个大于零的数字,前面的小于后面的就返回一个小于零的数字,只有当前面的等于后面的数字是才会返回零!

	char* a = "abcd";
	char* b = "abd";
	if (a > b)
	{
		printf(">");
	}
	else
	{
		printf("??");
	}

兄弟们来猜一猜这行代码会输出什么?
是不是兄弟们心中所想的那个>呢?
看到了这篇文章,不怕你学不会字符串函数!!!!!_第8张图片

我还故意放大这个??就是为了给兄弟们一种明显的视觉冲击!
这也就能给兄弟们一种启示,我们这里比较的并不是字符串的长度,那比较的是什么呢?
这里就直接告诉大家了:我们比较的是对应位置上的ASCII码值,从头往后比较,只要有一位有了比较结果我们就直接返回,如果比到最后两段字符串都是\0那么他们就相等!

int mystrcmp(const char* a, const char* b)//参数const大家应该知道什么意思了!
{
	assert(*a&&*b);//断言
	while (!(*a - *b)&&*a&&*b)//这里给大家重点讲一下哈
		//我们说的比较是按照位置一位一位往下比较,那么中间就必然会有循环,同时又要往下
		//那是不是也肯定会有地址的偏移?我们想一想什么时候我们会停止偏移呢?
		//是不是只要我们所掌握的信息足够支撑我们进行判断的时候我们就停下了?
		//那什么时候我们就停下了呢?
		//一种就是当我们只要有,而且不管是一个还是几个,只要有字符串走到了末尾,我们就没有办法
		//继续偏移下去了,还有就是对应位置上的字符不相等了,我们是不是就可以做出判断?
		//这不就是我们的循环条件嘛?
	{
		a++;
		b++;
	}
	return *a - *b;//我们现在跳出了循环,按照返回参数是不是理应返回一个整型?
	//我们知道字符类型从某种意义上也算是整型家族,因为只要是字符都有对应的ASCII码值
	//而这个数值就是整型啊!因此我们比较的就是字符的ASCII值!
	//这里我们直接对地址进行解引用,然后做差,得到的就是对应ASCII码的差值
	//以零作为分界线,很好判断,而且也少了相当大的分类的书写量!!
}
int main()
{
	char* a = "abcd";
	char* b = "abd";
	int ret = mystrcmp(a, b);
	//其实下面的部分我们还可以再写一个函数,知识我图个省事就没有写
	if (ret > 0)
	{
		printf(">");
	}
	else if (ret < 0)
	{
		printf("<");
	}
	else//最后一种情况就是ret为零啊
		printf("=");
	return 0;

这里直接上代码,有不放心的兄弟们可以直接去编译器里面测试!

5.strstr

在这里插入图片描述
看到了这篇文章,不怕你学不会字符串函数!!!!!_第9张图片

strstr函数的作用我就用代码的形式告诉大家:
这个函数就是判断是否是包含关系的,也就是说判断第二个参数所对应的字符串是否是第一个参数所对应字符串的子串!
如果是,那么函数将返回第一个参数中和第二个参数所对应的那个起始位置的地址,是不是听着听迷糊的,其实我们都不要担心这个的,真正使用的时候才不会有这么多烦恼呢,咔咔就开始敲代码!不过就是从第一个字符串中打印出与第二个字符串重叠及以后的部分呗,这有什么难的?
其实这个代码真的很难啊!!!!!(瞬间脸肿!)
那我来回忆一下,顺便帮助兄弟们分析一波,找我们之前说的,函数的第一个参数应该就是我们说的母串的首字符地址,第二个参数则是字串的首字符地址,我们返回的还是母串中于字串重叠部分的首字符地址,那么很显然,我们是不是就应该从母串的第一个字符往后面找?找什么?找的是能和字串的第一个字符匹配的字符,如果匹配上了那我们是不是就应该让母串与字串都各自++,那兄弟们我们来感受一波,这是不是就是呼之欲出的那个循环?,人生不可能总是一帆风顺的,那如果不匹配呢?不匹配的话说明,那个开始的字符串并不是我们所期待的那个真命天子,那我们还不果断换赛道,及时止损?这时候我们要做的就是避坑,都已经上过一次当了,就没必要继续下去了,那如果我们把母串和字串重新归零是不是无论再来多少次,最终我们都还是会得到那个相同的错误的结论?同时因为我们要判断的是字串在母串中的存在性,所以我们的子串在试过一次错后必须归零。而我们的母串呢?不需要归零,只需要从刚才我们开始的位置往后一位,继续重复试错就行!综上所述,我们是不是需要创建一个变量记录下与子串第一位匹配的那个地址?只要是循环,我们就应该想出我们退出这个循环的条件吧,现在我们有没有一种可能就是我们的母串中压根就没有这个子串呢?这样一来是不是无论我们刚才我们的创建下来记录地址的变量如何变,都i永远无法找完我i们的子串?但是总有一天,总有一天,我们的母串会来到“\0”这个位置,这就是我们结束循环的契机之一!还有一种可能就是我们的子串一直与母串匹配上,一直匹配到了最后那个“\0”此时母串中所对应的那个是不是“\0”已经不重要了,因为我们已经确定了在母串中有我们所需要的子串内容!所以我们还可以说另一个结束循环的条件就是子串来到了“\0”!
那上面我们侃侃而谈了这么久,究竟对不对呢?实践出真知!

char* mystrstr(const char* a, const char* b)//与原函数保持高度一致
{
	char* s1 = a;//s1,s2就是我们之后要动态比较的两个字符的地址
	char* s2 = b;//同时我们为了能够找到字串的首字符地址,在下面的代码里面
	//不会对b进行任何更改!
	char* str = a;//a的起始位置也保存一份吧!
	//我们之前不是说过在解引用一个指针之前最好都是需要断言一下的嘛?
	//那为什么在这里我们不进行断言呢?
	if (!*b||!*a )//a,b无非就是四种排列组合,我们所担心的是其中的三种
		//也就是说只要a,b中有一个空指针我们就麻烦,那我们不妨思考一下
		//就假设三种情况发生了一种,字串为NULL,那我们知道空集是所有集合的子集
		//这种情况我们直接返回母串的首字符地址进行打印
		//如果母串本省就是空指针大头,那无论字串的内容是什么,都只可能在母串这边找到
		//NULL这时我们还是返回母串的首地址,是不是也就是返回了一个NULL?
		//那我们希望达到的效果就是只要有任意一个空指针我们就返回str,究竟是用||还是&&呢?
		//如果空指针的话那表达式为假,我们无论如何是进不了循环的,因此我们加上一个!
		//那性质就变了,只要有一个空指针那么条件为真,如果使用&&代表着只要有一个
		//条件为假那么表达式就为假,这与我们的逻辑不符合,因此我们使用||
	{
		return str;
	}
	while (*str)//这里的str代表着有可能返回的母串中的首字符地址,那么我们想想是不是
		//只要我们的母串都走完了还没有找到我们期望的那个地址,是不是就应该鸣金收兵?
		
	{

		s1 = str;//这里兄弟们可以等到最后再回上来看
	//回到这行,此时我们的起始位置是完成了++,可是我们进行偏移的那两个位置怎么办呢?
		//他们现在已经不知道偏移到哪里去了呀,所以这时候记录的意义就体现出来了
	//我们只需要赋值操作就可以继续判断了
		s2 = b;//这里我们的字串是需要回到字串的第一个地址的
		while (*s1 && *s2 && !(*s1 - *s2))//如果啊我们的*s1和*s2相等
			//那他们两个相减得到的就是零,此时我们需要进入循环,判断他们后面的
			//字符是否一致,因此我们给零加上一个!,让否变为真从而进入
			//我们之前也说过需要的是两个动态的指针,分别比较母串与子串字符是否相等的
			//这里的s1,s2就是这个效果,当他们两个中只要有一个走到了\0,这个程序的作用也就起到了
		{
			s1++;//只有*s1 = *s2并且都没有走到\0时,才会进行偏移的操作
			s2++;//
		}

		if (!*s2)//按照道理我们是希望能找到字串的,那么怎么判断是否有字串呢?
			//如果啊按照上面的程序,只要字符相等,我们就进行偏移,那是不是只要
			//子串偏移到了\0的时候就能证明字串之前的内容在母串中都存在呢?
			//所以这就是我们的答案,还是需要!来做一个修正或者我们也可以直白一点
			//if(*s2=='\0')
		{
			return str;//返回的就是我们之前说的起始位置
		}
		str++;//但是我们想啊起始位置不可能从第一次进入循环就有吧?就算你有这个运气,那如果
		//后续的内容不匹配,是不是还是白忙活?所以我们的对于这个起始位置也是需要进行偏移
		//以及记录的!如果我们在上面的程序中并没有返回函数值,说明我们上一次记录的那个
		//起始位置并不是我们所希望的那个地址,那我们怎么办?起始位置继续往后面偏移一个单位
		//继续找!!这时候我们往上面看!
	}
	return NULL;//那我们如果在上面并没有返回任何值的话,也就是说子串在母串中并不存在
	//此时我们就可以潇洒的返回一个空指针!
}
int main()
{
	char* a = "Nothing is impossible";//这句话送给大家!
	char* b = "is";
	char* p = mystrstr(a,b);
	printf("%s",p);//这次我们就正常打印吧!
	return 0;
}

诶呀,光是这个思路的思考就很吃力,更不要说讲解了,希望兄弟们喜欢的可以点点赞!!
那最后我在对于这个做法提一些自己的看法:
1.我一开始自己思考的时候思路并不是这样的,因为这个做法的方式就是起始位置从第一位一直往后偏移,也就是我们写的str++这里说真的不太符合我的惯性思维,我所想的时什么呢?我想的是能不能通过一个循环来找到和我们字串的首字符一样第一个位置接下去进行判断,在这之后才是第二个第三个……当然了相比我说的方法,这个写出来的大概是更好的,因为我并没有写出那个方式的解法,说到底还是我的水平有限,有想法的兄弟们不妨也跟着试试
2.就是关于***!***的使用我们一般不太常见这种写法,我们一般都是很直白的写出判断的语句,这种“炫技”还是见得不多,但是我相信之后我们也能写出这样华丽的代码!

6.strtok

怎么样?看完阿涛上面的讲解是不是感觉自己的CPU都烧掉了?这是正常现象,我也是想了好久好久才有了这一丝丝领悟的,所以兄弟们不要气馁!!!
接下来的两个我们之讲解使用,暂时不说模拟实现了(事实上是因为原理有些复杂,我还需要一段时间才能够很好的理解,才有把握给兄弟们讲好!)。
看到了这篇文章,不怕你学不会字符串函数!!!!!_第10张图片
上面的讲解来自于万能的百度百科,我顺手就借用了一下!
那么正题来了strtok函数究竟还有什么作用呢?
就比如这个吧:[email protected],我们假设这就是一个邮箱,那这个邮箱是不是由三部分组成:第一部分的@和.这两个符号把整体分成了三段,那我们strtok函数的功能就是棒打鸳鸯,就是能够把这三段按照我们给的分隔符给分成三段,那么接下来我将在代码中给大家讲解!

int main()
{
	char* p = "[email protected]";//我们可以看到在我们要切割的这个字符串前面我们并没有
	//添加const这是因为从逻辑上面我么是要对这段字符串进行切割的
	const char* sep = ".@";//这里就是设置了我们所说的分隔符,存放的还是首字符的地址

	char arr[30];
	char* str = NULL;
	strcpy(arr, p);//因为我们是需要对原字符串进行切割的,但是谁知道未来的某一天我们是不是还是需要
	//这个字符串呢?因此我们对这段字符串进行了拷贝,以备不时之需
	for (str = strtok(arr, sep); //这里我们开始重点讲解strtok的实现机制
		//首先我们第一次使用strtok函数给它传参,第一个参数就是我们需要切割的那个
		//字符串首字符地址,而第二个参数就是我们说的切割符号的地址,
		// 就我们刚才使用的情况来看顺序对于这个函数似乎并不重要,重要的是你要确保
		// 在分隔符字符串里面有我们需要切割的符号,甚至于你偷偷摸摸多加上那么几个也是无关痛痒的
		//当然了我们传过去参数并不是闹着玩的,是要有作用的,第一次我们是从首字符开始往后找,一直找到在我们提供的
		//字符串中存在的字符,停下来,并且把这个字符改成'\0';然后我们从哪里开始就返回哪里的地址
		str != NULL; //这里讲的就是我们已经完成了所有切割,就停止切割
		str = strtok(NULL, sep))//这里我们给strtok函数传过去的是一个空指针
		//这是什么意思呢?我们知道刚才我们已经完成了第一部分的切割,不是
		//刚才我们偷偷把第一个找到的分隔符改成了'\0'嘛,那么我们第二次使用这个
		//函数的时候就从那个'\0'开始找到他下面一个字符,继续往后面找下一个分隔符
		//当然了我们刚才所说的一切都是基于我们已经切过了一次的前提之下
	{
		printf("%s\n", str);
	//现在我们已经完成了所有切割如果再次使用,只会返回一个NULL
	}
	return 0;
}

现在一定有兄弟对那个循环不是很理解,我们使用循环意义是什么?如果我们需要切很多段,难道我们还要手动输入strtok函数吗?没有那个道理的,我们使用循环,初始化中就是第一次使用函数
判断条件保证了我们并没有切完这个字符串,调整部分传的是空指针,也就是我们说的开始了从’\0’往后面找的过程!
那如果我们正切得好好的想切另外一段字符串了应该怎么办?很简单啊,我们就给strtok传另一个参数,只要他不是空指针我们就开始了另一段切割了!

7.strerror

看到了这篇文章,不怕你学不会字符串函数!!!!!_第11张图片
最后这个函数就是给我们提供了可以知道我们犯了什么错的工具!

printf (": %s\n",strerror(errno));

大致就是这样的一个形式:
这里的errno就是错误代码,你单单打印一个错误代码,最终只会给你一个数字,但是谁知道你这个数字是什么意思呢?这就需要我们今天的最后的主角了: strerror,给它传上errno他就能以英文的形式给你打出错误的原因,当然了我们还需要知道errno 是需要有文件 #include 的!而strerror的头文件则是 #include
有一个跟它的作用很像的函数:
看到了这篇文章,不怕你学不会字符串函数!!!!!_第12张图片
这个函数的作用大概就是:printf+strerror,当然了我们这个具体的使用还是根据具体的情景,如果你需要打印出来,那可以使用perror,如果你只是想要那个错误,就可以使用strerror!
好了,这是我第一次写下万字的博客!
还是那句话希望我的这篇博客可以帮到一些兄弟们,我们要一起加油!!!!!!!
百年大道你我共勉!!!

你可能感兴趣的:(c++,算法,开发语言)