前言
上一文,学习了字符串相同函数的使用,在某些场合,数据的类型不限于字符串,有可能是整形,浮点型甚至是自定义类型,所以内存函数(memcpy、memmove)就能实现数据的拷贝和移动
本文依就从函数的使用和模拟实现来介绍内存函数
一、memcpy
1.介绍
函数的作用是拷贝
函数的原型是含有三个参数,第一个参数为拷贝的目的地,第二个为拷贝的来源,第三个为拷贝的字节数。它将返回拷贝后数据的起始位置
同样的,必须包含memory.h或者string.h头文件
2.使用
接下来通过一个简单例子,理解memcpy的使用
创建了一个结构体类型,将s1内容拷贝给s2
memcpy函数的第一个参数为目的地地址,第二个参数为来源地址,第三个参数为来源内容的字节
3.模拟实现
思路:
要实现任意类型,那么函数的前俩个参数必须是void* 这样才能接收各种参数,其返回类型也必须是void*
拷贝---赋值,每次赋过去一个字节,之后让指针往后走一字节。
由于void*指针不能加减,所以必须将其强制类型转换为char*
那就来看看代码吧
void* my_memcpy(void* des, const void* sour, size_t num)
{
assert(des && sour);
void* head = des;
while (num--)
{
*(char*)des = *(char*)sour;
des = (char*)des+1;
sour = (char*)sour+1;
}
return head;
}
二、memmove
在strcat、strcpy中,不能自己追加自己,因为会把'\0'覆盖掉
类似的,memcpy想要拷贝自己的内容,也会出现失败
例如,有以下代码
我们想得到的预期结果应该是1 2 1 2 3 4 7
而实际上却得到 1 2 1 2 1 2 7
原因也很简单,在memcpy拷贝时,是一个字节一个字节的拷贝
在拷贝第三个数字,原本的数字3已经被覆盖为1,所以就得不到想要的结果
所以想要得到正确的结果就可以用memmove
1.介绍
这回就能得到我们想要的结果1 2 1 2 3 4 7
memmove函数参数有三个,第一个是目标位置地址,第二个是来源地址,第三个参数是拷贝的字节数目,返回类型是void*。该函数可以接收或者返回任意类型的数据
2.模拟实现
思路:
对于一个数组内容为
1 2 3 4 5 6 7,我们想将3级往后的3个元素拷贝到1 ,从3开始一个字节一个字节的拷贝是没有问题
但是想将1及往后的4个元素1 2 3 4 从1一开始拷贝到3 4 5 6上,就无法实现,其原因和strcat类似,会出现字符串覆盖。但是我们可以反过来,先拷贝4 ,在拷贝3,在拷贝2,最后拷贝1,就不会出现覆盖的问题。
根据这个想法,我们把元素分成俩种情况
- 从前往后拷贝--------从源 起始位置一个一个拷贝至最后一个元素
- 从后往前拷贝--------从源 某位置一个一个往前拷贝
上述就可以解决覆盖的问题
还有一类不存在覆盖,即从前往后拷贝和从后往前拷贝不影响结果,我们可以将这一类归到俩种的任一种。本文贵在第二种,代码较为简洁。
举例:从后往前拷贝,条件是des<sour
就有这些铺垫,那么我们就可以来模拟实现
void* my_memmove(void* des, void* sour, size_t num)
{
assert(des && sour);
const void* head = des;
//从前往后移动
if (sour > des)
{
while (num--)
{
*(char*)des = *(char*)sour;
des = (char*)des + 1;
sour = (char*)sour + 1;
}
}
else//从后往前拷贝
{
while (num--)
{
//找到最后的位置
*((char*)des + num) = *((char*)sour + num);
}
}
return head;
}
三、总结
在拷贝时,不要使用memcpy自己拷贝自己,这种情况用memmove。
memmove的思想依旧涉及较高的指针,值得大家练习。
最后,感谢大家的阅读。
我是小凡,欢迎大家提出宝贵的见解。
代码++
offer++