> 文档中心 > 【strlen函数的使用及strlen函数的三种模拟实现】· C语言详解库函数篇(一)

【strlen函数的使用及strlen函数的三种模拟实现】· C语言详解库函数篇(一)


大家好,我是Duoni!


 开始前言


博主介绍:一位不愿透露姓名的艺术生跨界分子

学习阶段:C语言进阶

信念支撑:业精于勤,只要足够肝,世间就没有跨不了的界!

阅前请求:博主自愧没有任何计算机基础,之所以接触于此,是因为本台电脑频频掉链子,奈何本人骨子里不愿服输的一根筋气质,我励志将自己弄坏的自己修的精神贯彻到底!至此偶然间就接触到了程序,从此便一发不可收拾,日夜沉迷。

我所分享的博文可能没有大佬们优化到极致的最优解,或是妙不可言的神码。但我可以确定的是,我的博文绝对是通俗易懂的,哪怕是小白。我希望在这里记录下我成长的脚印,同时也渴望得到各位大佬们的建议和斧正,就让我们一起前进吧!


介绍完啦!那么我们接下来就......

 发车!


目录

 开始前言

一、【strlen函数】双连问

(一)、strlen函数是干什么的?

1、strlen函数原型分析

           (二)、strlen函数是怎么用的?

  二、【strlen函数】的三种模拟实现方式

                               (一)、计数器法

                         (二)、递归法

                   (三)、指针法

                三、使用【strlen函数】的易错点及延申

       (一)、strlen函数与sizeof操作符的区别

  (二)、延申·【sizeof操作符】的使用场景

四、总结



一、【strlen函数】双连问

在学习到数组操作与字符串操作知识点时,大家是否会遇到使用【strlen函数】or【sizeof操作符】求元素个数的苦恼!在【数组】情况下是该使用【strlen函数】还是【sizeof操作符】呢?在【字符串】情况下又该使用【strlen函数】与【sizeof操作符】中的哪种呢?

当然,之所以会产生这一困扰是有原因的。接下来就跟随我往下一步步刨析吧!本篇在初始阶段本想向大家分享【strlen函数】的三种模拟实现方式。

 

 但在撰写的过程中,我逐渐发觉【strlen函数】与【sizeof操作符】有着一定的相似点,但又不能作用于同一数据对象。所以,接下来我会在侧重【strlen函数】知识点分享的同时,也延申一点【sizeof操作符】的知识点。

那么我们开始吧!


 

(一)、strlen函数是干什么的?

strlen函数是c语言【库】中的一个函数,当然同时也在各种语言中存在着。

【strlen函数】所作的是一个计数器的工作,它从被指定内存的某个位置,逐个向后扫描并计数,直到它碰到'\0'时才会停下,并且返回这一过程中读取到的'\0'以前的一个数位(和),也就是这一段空间中元素的个数。它不在乎该元素的类型大小,它只在乎这一段内存中的元素个数是多少位。

1、strlen函数原型分析

那么到底是什么神仙函数能如此敬业呢?让我们来看看它的函数原型吧:

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函数用起来很省心,你只需要将目标字符串名放入函数调用符中即可。随后,我们需要创建一个整形 变量去接收strlen函数的返回值。

includeint 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函数是如何被轻松的使用了。

那么,这时候的我们也该沉重的思考一下:

设计者是怎么设计出这个函数的?

函数的内部是怎么运作的?

我能不能和设计者一样厉害?

思考结束,接下来我们实践出真理吧!

 


二、【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】函数的三种模拟实现后,我们接着来探讨一下文章开头的问题:

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这个结果~ 


(二)、延申·【sizeof操作符】的使用场景


所以,术业有专攻:在遇到字符串与数组类型该如何求长度的问题时。

一定要坚定的知道:字符串用strlen函数求长度,数组类型用sizeof操作符求长度!


四、总结


如果你看到这里了,那么我要跟你说声谢谢!

业精于勤荒于嬉,行成于思毁于随。虽说我非科班出身,但我却无限憧憬成长后的自我,我愿压上我全部的精力去赌一瞬化简成蝶!亲爱的朋友们,我们一起前进吧!

在接下来的时间中,我会以每周两篇的数量进行更新,分享我的知识与感悟,如果喜欢博主,就毫不犹豫的关注我吧!我们一起共进!!