一、memcpy函数的使用与实现
前面我们学习了字符串函数,里面的strcpy函数和strncpy函数是实现拷贝字符串的功能,不同的是前者是将整个字符串拷贝,后者是可以指定拷贝的字符个数。但是我们的数据类型有多种,那么当我们需要拷贝的对象是其他的数据类型,这时候我们要咋办呢?那么有没有那么一种函数,可以实现对任何数据类型都可以实现拷贝功能的函数呢?
有的兄弟,有的。我们接下来要学习的这个函数就是了。
memcpy函数,它是对内存块的内容进行拷贝,无论内存存放的内容是什么类型,都可以通过内存来实现拷贝。
使用memcpy函数也是包含头文件<string.h>
函数原型如下:
void * memcpy(void * destination, const void * source,size_t num );
其返回类型是一个void指针,然后其两个参数也是void类型的指针,这是因为我们拷贝的数据类型是多样的,不是固定的了,第三个是一个无符号整型,这个参数就是表示要拷贝的内容的字节数。
其和strcpy函数一样,返回的是目标空间的地址,也就是第一个参数的地址。
下面我们总结一下这个函数:
其从第二个参数的地址开始,然后向后走num个字节,然后将这num个字节的数据复制到第一个参数指向的位置。
这个函数和前面学习字符串函数不太一样的就是,其不会因为遇到字符\0就停止,其拷贝多少数据完全是按第三个参数来决定。
如果说第一个参数和第二个参数有任何的重叠,复制的结果都是未定义的。
下面我们来使用这个函数看看其效果:
运行结果:
可以看到其将arr2数组的内容拷贝到了arr1数组中。
下面我们试着模拟实现这个函数把
和前面一样函数命名:my_memcpy
函数参数和其原型一样。
因为参数中有有两个是指针,所以我们对这两个指针进行断言,注意的是要记得包含头文件。
然后要返回的是第一个参数的地址首地址,那么我们先创建一个指针变量来存储。
由于数据的类型是不定的,那么我们可以将他们转换为字符指针,一个字节一个字节的走,这样就可以拷贝全部内容了。
那么我们创建一个循环,循环次数就是要拷贝的字节数,一次循环就拷贝一个字节,然后,前面两个参数的位置就往后走一个字节,那么就先将两个参数转换为字符指针,然后进行+1操作。
最后拷贝完后就将一开始用来存储目标地址的指针返回即可。
代码如下:
运行如下:
上面的我们说到如果说第一个参数和第二个参数有任何的重叠,复制的结果都是未定义的,那么这种情况的怎么进行拷贝呢?
这种情况就需要我们的memmove函数了。
二、memmove函数的使用与实现
上面提到了,memmove函数可以说是memcpy函数的升级版,它不仅可以实现memcpy函数的功能,还可以处理存在内存重叠的情况,使用其需要包括头文件string.h
其原型如下:
void * memmove(void * destination , const void * source,size_t num);
可以发现其和memcpy函数的原型是一样的,都是三个参数,参数的类型也是一样的,函数的返回类型也是一样的。
两个函数的区别:
memmove和memcpy函数差异就是memmove函数处理源内存块和目标内存块是可以重叠的
如果源空间和目标空间出现重叠,那么就需要使用memmove函数了。
下面我们使用memmove函数看看其效果:
先看其处理内存没重叠的情况:
运行结果:
下面我们看看有内存重叠的情况:
运行结果:
可以看到其完美的解决了内存重叠的情况
下面我们来模拟实现这个函数
函数的名字还是老样子:
my_memmove
函数参数和其原型一样。
思路:
还是一样,其参数有指针,那么我们先对其进行断言,判断其传入的不是空指针
然后我们创建一个指针变量来存储目标空间的地址,用来函数完成拷贝后的返回
这部分和上面对memcpy函数的差不多,主要是对于内存重叠这个问题我们该如何去处理。
我们可以通过两个指针参数来判断,当目标空间的地址在第二个参数的后面的时候,那么我们就从后往前拷贝,我们前面知道,数组在内存中的空间是连续的,并且地址和数组的下标是正相关的,所以当第一个参数的地址>第二个参数的地址的时候那么我们就从前面开始往后拷贝,而当第一个参数的<第二个参数的地址的时候,那么我们此时就从后往前拷贝,从前往后拷贝我们在前面对memcpy函数的模拟实现已经学习过了。
我们主要是如何实现从后往前拷贝,关键就是如何找到第一个参数的数组的末尾和第二个参数数组的末尾,我们还有第三个参数,其就是我们要拷贝的字节大小,那么我们使用当前的地址去+num-1那么就很巧妙的是末尾的地址了。然后我们让num--那么此时两个数组的地址都往前走了,那么就可以实现从后往前走了
代码如下:
函数测试:
三、memset函数的使用
memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
其原型如下:
void * memset(void *prt ,int value ,size_t num);
它的第一个参数是要设置的数组的首元素地址,第二个参数是要设置的值,第三个参数是要设置的字节数。
这里要注意的是,第二个参数的类型是整型类型,那么我们在传入参数的时候,就使用整型数据,不过我们也可以传入字符型,因为字符型数据在本质上是其ascll码值,那么也可以说是整型。
下面我们使用其对数组进行设置字符型数据:
运行结果:
可以看到其将字符串数组的全部内容都变成了字符x。
四、memcmp函数的使用
这个函数和我们前面学习的strcmp函数有点像,strcmp函数是可以根据给的字节个数来比较字符串的大小,那么memcmp就是可以不定数据类型进行比较了。
使用这个函数需要包含头文件string.h。
其原型如下:
int memcmp(const void * dest, const void * soure ,size_t num);
可以看到memcmp函数的参数和strcmp函数的参数是一样的,其返回类型也是一样的。
下面我们使用其比较两个整型数组:
运行结果: