大家好,我是Duoni!
博主介绍:一位不愿透露姓名的艺术生跨界分子
学习阶段:C语言进阶
信念支撑:业精于勤,只要足够肝,世间就没有跨不了的界!
阅前请求:博主自愧没有任何计算机基础,之所以接触于此,是因为本台电脑频频掉链子,奈何本人骨子里不愿服输的一根筋气质,我励志将自己弄坏的自己修的精神贯彻到底!至此偶然间就接触到了程序,从此便一发不可收拾,日夜沉迷。
我所分享的博文可能没有大佬们优化到极致的最优解,或是妙不可言的神码。但我可以确定的是,我的博文绝对是通俗易懂的,哪怕是小白。我希望在这里记录下我成长的脚印,同时也渴望得到各位大佬们的建议和斧正,就让我们一起前进吧!
介绍完啦!那么我们接下来就......
发车!
目录
开始前言
一、【strlen函数】双连问
(一)、strlen函数是干什么的?
1、strlen函数原型分析
(二)、strlen函数是怎么用的?
二、【strlen函数】的三种模拟实现方式
(一)、计数器法
(二)、递归法
(三)、指针法
三、使用【strlen函数】的易错点及延申
(一)、strlen函数与sizeof操作符的区别
(二)、延申·【sizeof操作符】的使用场景
四、总结
在学习到数组操作与字符串操作知识点时,大家是否会遇到使用【strlen函数】or【sizeof操作符】求元素个数的苦恼!在【数组】情况下是该使用【strlen函数】还是【sizeof操作符】呢?在【字符串】情况下又该使用【strlen函数】与【sizeof操作符】中的哪种呢?
当然,之所以会产生这一困扰是有原因的。接下来就跟随我往下一步步刨析吧!本篇在初始阶段本想向大家分享【strlen函数】的三种模拟实现方式。
但在撰写的过程中,我逐渐发觉【strlen函数】与【sizeof操作符】有着一定的相似点,但又不能作用于同一数据对象。所以,接下来我会在侧重【strlen函数】知识点分享的同时,也延申一点【sizeof操作符】的知识点。
那么我们开始吧!
strlen函数是c语言【库】中的一个函数,当然同时也在各种语言中存在着。
【strlen函数】所作的是一个计数器的工作,它从被指定内存的某个位置,逐个向后扫描并计数,直到它碰到'\0'时才会停下,并且返回这一过程中读取到的'\0'以前的一个数位(和),也就是这一段空间中元素的个数。它不在乎该元素的类型大小,它只在乎这一段内存中的元素个数是多少位。
那么到底是什么神仙函数能如此敬业呢?让我们来看看它的函数原型吧:
size_t strlen( const char *string )
strlen函数原型解析:
1、strlen函数的返回值是【size_t],这是一个无符号类型的整数(unsigned int)。
(可以这么理解:我们要strlen函数去帮我们数一下目标字符串中元素的数量,它只能兢兢业业的返回这个字符串有几个元素,但是,它绝对不可能返回一个负数。如果它真的返回了一个负数,那我们可以直接说它不靠谱!)
2、它的函数参数为:(const char* string)我们可以怎么理解:由const修饰的指向string(字符串)首元素地址的字符指针。
看到这,真相也大白了:strlen函数所作用的对象是字符串!原因也很直接,因为函数规定:由某个指定位置向后逐个扫描计数,直到遇到'\0'停止并返回计数。我们回想一下:数组与字符串二者谁是以’\0‘结尾呢?
额……真相只有一个~
只有字符串是以:'\0'结束的!
切记不要与数组搞混了!那么接下来我们来探讨探讨strlen函数是怎么用的吧!
strlen函数用起来很省心,你只需要将目标字符串名放入函数调用符中即可。随后,我们需要创建一个整形 变量去接收strlen函数的返回值。
include
int main()
{
int count = 0;
char vate[] = "You can do it!";
count = strlen(vate);
printf("vate的长度是:%d", count);
return 0;
}
在这里卖个关子,以下有四个选项,大家认为vate的长度会是多少呢?
A.11 B.14 C.13 D.12
公布答案:
或许有一些新同学会疑惑:我数了里面只有11个字符呀!为什么会是14呢?
(回首往事,我也如此疑惑,后来才知空格也算一个字符!)
好啦!我们已经亲眼瞧见过strlen函数是如何被轻松的使用了。
那么,这时候的我们也该沉重的思考一下:
设计者是怎么设计出这个函数的?
函数的内部是怎么运作的?
我能不能和设计者一样厉害?
思考结束,接下来我们实践出真理吧!
首先,第一个方法是:计数器法,这个也是最为直观的方法,我最喜欢了!
int my_strlen(const char* vate)
{
int count = 0;
while (*vate++ != '\0')
{
count++;
}
return count;
}
int main()
{
int count = 0;
char vate[] = "You can do it!";
count = my_strlen(vate);
printf("vate的长度是:%d", count);
return 0;
}
为了能更有趣的吃透理解清这个模拟方法,我来举个例子吧!
my_strlen函数中,字符串就像一群在桥下游过的小鸭子,而count变量就像一个在桥上数鸭子的小朋友,每经过一只鸭子,小朋友用指头计一个数。但鸭子总会全部游过去,小朋友该怎么停止计数呢?
妈妈告诉他:小军呀!只要在鸭群中没出现小鸡,你就继续数。但如果看见了,你可要赶紧停下来呀!然后把前面的数记在本本上,交给我看。
当然,例子中的“小鸡”指的就是'\0'。
思路阐述:
1、字符串在传参时,传出的是字符串首元素的地址。函数接收后(接收也是字符串的首地址),它可以供函数持续的访问,直到到最后的'\0'处。
例子奉上:
void my_strlen(const char* vate)
{
printf("%s\n", vate);
}
int main()
{
int count = 0;
char vate[] = "You can do it!";
my_strlen(vate);
return 0;
}
输出结果:
提示:(千万不要对字符串首元素进行修改,不然你会遇上这样的麻烦)
我来试试~
void my_strlen(const char* vate)
{
vate = 'wo';
printf("%s\n", vate);
}
int main()
{
int count = 0;
char vate[] = "You can do it!";
my_strlen(vate);
return 0;
}
输出:
很奇怪,为什么会一片空白呢?
其实呢!很有趣:字符串首元素地址的存在,以程序中的vate字符串为例。
在主函数中,vate字符串的空间已经在内存中被开辟,如果按照正常情况:传址——接收——顺序访问,过程应该是很丝滑的。而在 函数中对首元素的修改,让这个字符串指针与后面数据断开了联系,你想让它在修改后再输出原来的数据,这几乎是不可能的。
举个栗子:字符串的首元素就像羊群中的头羊,如果你把这只原头羊换做新的头羊,那么这个羊群就不会跟着你走了。
似乎扯远了,接下来分析剩下的思路:
2、有了第一点后,我们已经可以保证我们可以访问这个字符串的全体了,那么接下来我们就需要进行一个while循环条件的设定:如果没有访问到字符串中的‘\0‘(*vate++ != '\0'),就一直得逐个向后访问。
3、最后呢,我们需要安排一个变量,用于每一次进入循环后的++;因为能够进入到循环中,则说明这个元素是非\0的。
消化完第一种解法后,接着我们来看看第二种解法。相信【递归】对于大家来说并不陌生,在最初,我也曾迷惑过递归实现的基本原理是什么?
后来,偶然耳畔飘过一句:出来混迟早要还的!
顿时,我悟了。递归就像一把甩出的回旋镖,不论它飞出了多远,最后都会返回原点方向:一来一回。即是递归。
可能理解的太过生活化了,但任何基本原理都来源于生活,恒成立!
上代码:
int my_strlen(const char* vate)
{
assert(vate != NULL);
if (*vate == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(vate + 1);
}
}
int main()
{
int count = 0;
char vate[] = "You can do it!";
count = my_strlen(vate);
printf("vate的长度是:%d", count);
return 0;
}
输出:
接着我们来细细剖解代码的实现原理:
如果指针vate中的元素不等于'\0',那么进入到else语句:将指针往后移动一位。再次进入if语句中判断,如果找到'\0\,那么return 0。
是不是理解起来不太友好呢?没事,接下来我们直接上图理解:
千言万语,都汇聚在步骤图中了,小伙伴们理一理呀!递归并不可怕!
关于这个解法,这些小知识需要记住一下!
1、主函数将字符串名传入函数,而字符串名则代表的是字符串的首元素地址,故函数使用指针接收。
2、递归的使用一定要满足两个要点,才算是一个“入门级”的递归。(第一:要设置一个“出口”条件。第二:要让递归无限向出口条件靠近。)
终于还是逃不了指针,是的,指针的用处很广,而且很高效。但它也是一把双刃剑,用的好:指哪打哪!用的不好:指哪偏哪!
指针解法,上代码:
int my_strlen(const char* vate)
{
assert(vate != NULL);
char* vate_2 = vate;
while (*vate_2)
{
vate_2++;
}
return vate_2 - vate;
}
int main()
{
int count = 0;
char vate[] = "You can do it!";
count = my_strlen(vate);
printf("vate的长度是:%d\n", count);
return 0;
}
输出:
不得不说:指针这小兄弟能处!
紧接着,我们趁热打铁,来瞧瞧指针解法的实现是怎么实现的:
思路阐述:此处利用到的是:指针的加减运算知识。先将vate首元素赋值给vate_2,让vate_2去找'\0',意图是让vate_2跑到字符串元素的最末尾,然后与vate中的首元素相减,所得出的就是它们的距离了,即元素个数。
注意点:
一定要觉察到:指针的减法,得出的并不是二者的差(即:13),而是二者之间元素的一个距离(即:14)。
上图理解:
好啦!这就是指针法的解题方法。
提一嘴:指针真的很重要,虽然会有点绕,但一定要静下心来吃透!
分析完【strlen】函数的三种模拟实现后,我们接着来探讨一下文章开头的问题:
strlen函数与sizeof操作符有什么区别呢?
其实,strlen函数是专门用来计算字符串元素的数量,而sizeof则是用来计算数据类型的大小,两者或许根本搭不上边,但因为C语言语法的自由度高,各位大佬们创造出了以下语句,便让sizeof操作符也可以计算出元素的数量:
sizeof(arr)/sizeof(arr[])
但同样的,以上的写法虽然让sizeof操作符有了计算元素长度的能力,但也仅仅只作用于数组类型。
1、数组名有两种情况下代表整个数组元素:sizeof(数组名)、&数组名。只要sizeof取得整个数组元素后,再除以数组的其中一个元素,就可以计算出数组内的元素数量。
2、如果sizeof用于计算字符串长度时,则会发生麻烦,而这个麻烦的引发者也正是'\0'.
看代码:
int main()
{
int count = 0;
char vate[] = "You can do it!";
count = sizeof(vate)/sizeof(vate[0]);
printf("vate的长度是:%d\n", count);
return 0;
}
结果:
为什么说是'\0'的锅呢?
因为sizeof计算的是元素类型的大小,它不像strlen函数,只计数'\0'之前的数位。sizeof在拿到字符串名后,就开始计算全部元素的大小,它才不做选择,它全要!
最后除以char类型的大小(1)后,就有了:15这个结果~
所以,术业有专攻:在遇到字符串与数组类型该如何求长度的问题时。
一定要坚定的知道:字符串用strlen函数求长度,数组类型用sizeof操作符求长度!
如果你看到这里了,那么我要跟你说声谢谢!
业精于勤荒于嬉,行成于思毁于随。虽说我非科班出身,但我却无限憧憬成长后的自我,我愿压上我全部的精力去赌一瞬化简成蝶!亲爱的朋友们,我们一起前进吧!
在接下来的时间中,我会以每周两篇的数量进行更新,分享我的知识与感悟,如果喜欢博主,就毫不犹豫的关注我吧!我们一起共进!!