还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数

【手把手带你学】字符串函数&内存函数

  • strlen
    • strlen的模拟实现
  • strcpy
    • strcpy的模拟实现
  • strcat
    • strcat的模拟实现
  • strcmp
    • strcmp的模拟实现
  • strncpy
    • strncpy的模拟实现
  • strncat
    • strncat的模拟实现
  • strncmp
    • strncmp的模拟实现
  • strstr
    • strstr的模拟实现
  • strtok
  • strerror
  • memcpy
    • memcpy的模拟实现
    • memmove的模拟实现
  • memcmp
    • memcmp的模拟实现
  • memset
    • memset的模拟实现

我们知道,编译器给我们提供了很多实现不同常用功能的库函数,它们给我们提供了很多便利。

但是怎么说它们都是别人家的函数,我们能不能自己来实现,把它变成我们自家的函数呢?

当然可以!只有想不到,没有做不到!

话不多说,先点个赞!再开始我们今天的学习之旅吧!
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第1张图片

下面我们就来一个个攻破它们吧!

首先我们看看我们今天要掌握的函数有哪些吧~

  • 求字符串长度
    strlen
  • 长度不受限制的字符串函数
    strcpy
    strcat
    strcmp
  • 长度受限制的字符串函数介绍
    strncpy
    strncat
    strncmp
  • 字符串查找
    strstr
    strtok
  • 错误信息报告
    strerror
  • 内存操作函数
    memcpy
    memmove
    memset
    memcmp

strlen

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第2张图片
strlen - 字符串长度
我们传给函数一个字符串的地址,它负责帮我计算字符串的长度。

  1. 字符串以’\0’结束,而strlen返回的是字符串中在’\0’之前出现的字符个数
  2. 传给函数的字符串应该以’\0’结束,否则返回的是一个随机值。(因为strlen会一直向后访问直到遇到’\0’为止)
  3. strlen返回值类型是size_t,一个无符号整型。

大家思考一下,下面的程序会输出什么值?

#include 
#include 
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf("hehe\n");
	}
	else
	{
		printf("haha\n");
	}
	return 0;
}

strlen(“abc”)的值比strlen(“abcdef”)的值小,所以屏幕中应该输出“haha”,是这样的吗?

大家要注意,strlen这个函数的返回类型是size_t,无符号整型,而无符号整型计算的结果应该是>=0的,所以程序最终输出的应该是“hehe”。

我们上可以看到打印的结果是“hehe”。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第3张图片

我们通过调试中的监视窗口可以看到,它其实是一个很大很大的正数。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第4张图片

strlen的模拟实现

弄懂了strlen函数之后,我们就来开始我们的第一个模仿创造之旅,写一个我们自己的strlen函数吧~

下面我们把自己模拟实现的strlen函数称为my_strlen函数。

首先我们写出my_strlen函数的使用场景。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第5张图片
然后进行函数的封装。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第6张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第7张图片

其实除了上面这种方法外,还有下面这两种方法。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第8张图片

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第9张图片

可以看到,这三种写法都能帮助我们实现my_strlen。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第10张图片

strcpy

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第11张图片

strcpy - 字符串拷贝
我们传一个目标字符串和源字符串给函数,让它帮我们把源字符串中的字符拷贝到目标字符串中。

  1. 源字符串必须以’\0’结尾。
  2. strcpy函数会把源字符串中的’\0’也拷贝到目标字符串中。
  3. 目标字符串的必须可变。
  4. 目标字符串的空间必须足够大。(确保能放源字符串)

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第12张图片

strcpy的模拟实现

strcpy的模拟实现我们在之前的文章中已经详细讲过,这里就直接上代码啦!

详情请看学了编程却写出错误代码?程序运行结果与想象不符?当bug出现时该何去何从,别担心,这篇文章统统告诉你!手把手带你调试代码,让bug原形毕露!常见的代码技巧这一节。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第13张图片

strcat

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第14张图片

strcat - 字符串连接
我们把目标字符串和源字符串一起传给函数,函数帮我们把源字符串接到目标字符串的后面。

  1. 源字符串必须以‘\0’结尾。
  2. 目标字符串必须可被修改。
  3. 目标字符串的空间应该足够大。
  4. 谨慎用strcat自己给自己追加。(自己给自己追加时,程序会覆盖掉’\0’,导致无限追加,程序崩溃)

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第15张图片

strcat的模拟实现

首先我们还是先写出函数的使用和封装。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第16张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第17张图片

strcmp

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第18张图片

strcmp - 字符串比较
我们把两个字符串传给函数,由函数帮助比较两个字符串的大小。

注意:我们这里比较的是字符串的内容,而非长度。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第19张图片
由上图,可以看到函数strcmp的返回值:

如果两个字符串相同,则返回0。
如果两个字符串不同,则比较第一个不同的字符的值,如果pt1的值小于ptr2的值,则返回一个<0的数,否则返回一个>0的数。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第20张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第21张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第22张图片

strcmp的模拟实现

step 1 写出my_strcmp的使用和封装。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第23张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第24张图片

上面的函数是长度不受限制的,所以我们传多长的字符串给函数,函数就会一直进行相应的操作知道达到了结束的条件。所以我们说这样的函数是不够安全的。

所以,库中又有了一些长度受限制的函数,它们相对来说就被认为是比较安全的函数。

strncpy
strncat
strncmp

我们可以看到,这三个函数相对于前面的三个函数,函数名中间都多了一个n,而这个n实际上就是num,即操作的字符个数。

strncpy

拷贝num个字符到目标字符串中。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第25张图片
如果我们想拷贝的字符数比源字符串长度大,则超出来的部分会默认填充’\0’。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第26张图片

strncpy的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第27张图片

strncat

连接num个字符到目标字符串后面。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第28张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第29张图片

如果源字符串长度小于我们希望连接的字符长度。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第30张图片

strncat的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第31张图片

strncmp

比较num个字符。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第32张图片

strncmp的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第33张图片

strstr

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第34张图片
strstr - 字符串查找
用于在一个字符串中查找子字符串,返回的是子字符串在该字符串中第一次出现的位置,如果不包含该子字符串,则返回NULL。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第35张图片
注意,strstr是查找ch2在ch1中第一次出现的位置。所以如果ch2在ch1中出现了两次,则只返回第一次出现的位置。

strstr的模拟实现

首先我们应该考虑到查找字符串时可能出现的两种情况。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第36张图片
第一种情况:首先看p1是否与p2相等,是则否p1++。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第37张图片

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第38张图片

是则p1和p2均++。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第39张图片
然后再继续++,直到遇到’\0’为止。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第40张图片

当然,如果是p1先遇到’\0’,查找也应该停下来。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第41张图片

情况1相对较为简单,一次查找就能找到。

下面我们看情况2。如果还是按照情况1那样往下走,当遇到下面这种情况之后,我们应该怎么办呢?
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第42张图片
这时候p2应该回到起始位置,同时p1应该回到p1开始查找的下一个位置,再重新进行查找。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第43张图片

所以我们应该先把这p1和p2要回归的那个位置保存下来。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第44张图片
当p1往前走时,s1也要跟着往前走。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第45张图片
p1和p2相等时,查找开始,s1应该记录下p1开始查找的位置的下一个位置。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第46张图片
这样当p1和p2不匹配时,p1就可以回到s1的位置重新开始查找。

下面我们用代码实现my_strstr。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第47张图片

当然上面的代码我们可以简化如下:
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第48张图片

strtok

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第49张图片
strtok - 字符串切割
把由分隔符分割的字符串分割成各个部分。

例如:123.111.222.383
按照.分割为
123
111
222
383

那么这个函数到底是怎样的呢?

  1. 参数delimiters指的是用作分隔符的字符集合。
  2. str指向的是一个被0个或多个分隔符分割的字符串
  3. strtok函数找到str中的下一个标记,并将更改为’\0’,同时返回一个指向这个标记的指针。(注意:因为strtok函数会改变str,所以我们传参时一般传的是一份临时拷贝并且可修改的内容。)
  4. 当strtok函数的第一个参数str不为NULL时 ,函数将找到str中的第一个分隔符,并保存它在字符串中的位置。
  5. 当strtok函数的第一个参数str为NULL时 ,函数从str中上次保存的位置开始,查找下一个分隔符。
  6. 当找不到分隔符时,strtok会返回NULL。

说了那么多,不知道看懂了没?

我们还是用代码来看看吧~
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第50张图片

那么像这样一段代码,我们可以用一个循环来表示,会显得更加好理解。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第51张图片

我们可以发现,每一次调用完strtok之后,函数会保存上一次函数调用时访问的位置。

这说明该函数中应该含有一些静态变量或者全局变量,函数调用完之后,这些变量并没有随着函数栈帧的销毁而销毁,从而使得strtok函数具有“记忆功能”。

当然由于使用了这个函数的实现是比较复杂的,并且相对来说并不常用,所以这里博主就没有尝试把它变成自家函数啦!有幸的uu们可以自己尝试一下哈!

strerror

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第52张图片
strerror - 错误信息
C语言中,我们会对一些常见的错误进行编号,并给上一个错误码,当我们把错误码传给strerror时,函数就会返回我们错误信息,告诉我们是哪里出了错。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第53张图片
我们可以看到每个错误码对应的错误信息,但是我们应该如何获得错误信息呢?

当库函数调用失败的时候,就会返回一个错误码。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第54张图片
所以,strerror是一个可以返回C语言内置的错误码对应的错误信息的函数。

此外,还有一个strerror的进阶版:perror,可以认为它==printf + strerror,即打印错误信息。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第55张图片

我们还是通过代码来看看。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第56张图片

memcpy

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第57张图片
memcpy - 内存拷贝
之前我们学过strcpy - 字符串拷贝,它只能对字符串进行操作,那么现在我们看看内存拷贝,它是对内存进行操作的,也就可以对各种类型的变量进行操作。

在这里插入图片描述

memcpy的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第58张图片

我们再看下面这段代码。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第59张图片
但是这能不能实现呢?

我们调试来看看。

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第60张图片
我们可以看到arr并没有变成我们希望的样子,因为前面要被拷贝的元素被覆盖了。

但是在C语言中,我们对memcpy的要求是只要完成了不重叠的内存拷贝就ok。(尽管现在一些编译器的memcpy也可以完成重叠的内存拷贝)

那么对于内存重叠的情况,我们应该怎么办呢?

这时候我们就要提一提memmove这个函数啦!

#memmove
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第61张图片
memmove - 内存移动
我们可以看到,memmove的参数和返回类型和memcpy实际上是一样的,但是它既可以完成不重叠内存的拷贝,也可以完成重叠内存的拷贝。

memmove的模拟实现

那么我们要实现内存重叠的拷贝,应该如何实现呢?

为了防止拷贝时重叠的内存块被覆盖,我们应该在内存被覆盖之前就将其拷贝到目标位置的,这里分两种情况。

在这里插入图片描述

那么我们具体应该怎么做呢?

我们可以根据dest的位置分两种情况进行讨论。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第62张图片

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第63张图片

从前向后拷贝,应该与memcpy的实现是一样的。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第64张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第65张图片
我们再来看看从后向前拷贝。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第66张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第67张图片

最后,把返回值写上,我们的my_memmove就完成啦!
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第68张图片
下面我们来验证一下~

从后往前拷贝:
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第69张图片
从前往后拷贝:
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第70张图片
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第71张图片

NICE!下一个!

memcmp

在这里插入图片描述
memcmp - 内存比较
比较内存中的num个字节。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第72张图片

memcmp的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第73张图片

memset

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第74张图片
memset - 内存设置函数
我们可以通过memset把指定内存中的内容放入我们指给定的值。
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第75张图片

memset的模拟实现

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第76张图片
以上这么多的函数,你都收入自己的函数库中了吗??

快来评论区!!!让我们知道你学会多少个库函数~~

其实库函数中关于字符串和内存的函数还有很多,大家感兴趣的可以一一去研究一下~
还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第77张图片
我们今天的内容就分享到这里啦!

如果你喜欢我的文章,别忘了点赞收藏加关注噢~

关注我,一起精进C语言!

还在用别人家的函数?别走!这篇文章教你把别人家的函数变成自己!手把手带你学会字符串函数和内存函数_第78张图片
本文相关代码已经上传至gitee啦!欢迎取阅~
https://gitee.com/fang-qiuhui/my-code/blob/ef5606d87a73e595436453a7e4b1ab0a0ff07bcd/practice_2021_9_21_Library_Functions/practice_2021_9_21_Library_Functions.c

你可能感兴趣的:(【手把手带你学C】系列,c语言,编程语言,库函数,字符串)