什么是memcpy函数
在cplusplus官网上是这样介绍的
这里的意思是memcpy会从开始位置复制若干个字节到终止的内存位置,这个函数在遇到\0时不会停下来,如果在复制的时候出现内存重叠的时候结果都是未定义的,也就是说我们有一个数组arr里面放有1,2,3,4,5,6个元素,现在我们把他复制20个字节到arr2数组里面,arr2里面就存放了1,2,3,4,5个元素,因为这个是按字节复制的一个整形占4个字节那么就是5个元素,如果把arr数组从首地址开始,复制到arr+2那个空间里面,这里就发生了内存重叠,结果就是未定义的。我们来举一个例子,来看看具体怎么使用。
# include<string.h>
# include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
int arr2[10] = { 0 };
int sz = 20;
memcpy(arr1, arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d", arr2[i]);
}
return 0;
}
这个函数包含的头文件是string.h,我们看看运行结果。
这里我们发现我们成功复制了arr数组里面的20个字节到arr2里面并成功打印出了1,2,3,4,5这个元素,我们这里数组给的空间比较大,后面空余的空间会自动补0。
接下来我们看看memcpy函数的模拟实现
memcpy函数的模拟实现
我们来看看这张图
我们看到这个函数需要传的参数都需要转换成void的指针,这是因为我们不仅仅只能拷贝整形数据,还要能拷贝字符型数据等,所以我们要用void指针,下面来看看代码
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* start, void* go_to, size_t num)
{
void* ret = go_to;
assert(start != NULL && go_to != NULL);
for(num ; num > 0 ;num--)
{
*(char*)go_to = *(char*)start;
go_to = (char*)go_to + 1;
start = (char*)start + 1;
}
return ret;
}
int main()
{
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
int arr2[10] = { 0 };
int num = 20;
my_memcpy(arr1, arr2,num);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
这里我们用start来代替官网中使用的scr,goto代替destination
void* my_memcpy(void* start, void* go_to, size_t num)
这里的参数就对应上面说的要变成void指针
首先把go_to的首地址存起来,我们最终是要返回go_to的,然后为了保证指针的有效性我们用assert来断言一下,防止指针为空然后下面就进入到拷贝环节了,我们用了一个for循环我们把他强制转换为char这是因为char*类型的指针解引用他只向后偏移一个字节,这样我们可以拿这一个字节做一个标准一个单位来完成我们整形,字符型等元素的拷贝。最后就是打印数组了。下面是memmove函数讲解,这个的模拟实现要复杂一些。
什么是memmove函数
我们可以看到在c++官网是这样定义的
这个就可以用来在有重叠的空间进行拷贝内容,我们来看看是怎么用的他包含的头文件还是string.h
# include<string.h>
# include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = 20;
memmove(arr+2, arr, sz);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果
这里我们在同一内存空间成功拷贝了,接下来看看模拟实现
# include<stdio.h>
# include<assert.h>
my_memove(void* start, void* go_to, size_t num)
{
void* ret = go_to;
assert(start != NULL && go_to != NULL);
if(start>go_to)
{
while(num-- != 0)
{
*(char*)go_to = *(char*)start;
start = (char*)start + 1;
go_to = (char*)go_to + 1;
}
}
else
{
while (num--)
{
*((char*)go_to + num) = *((char*)start + num);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memove(arr1, arr1+2, 5*sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行结果
这里我们需要知道是怎么拷贝的,我们需要分两种情况首先我们是从前往后拷贝,还是从后往前拷贝,通过分析在go_to小于start的时候就从后往前拷贝,当go_to大于start的时候从前往后拷贝这就是这个模拟实现最重要的地方,其他的就好理解了
*((char*)go_to + num) = *((char*)start + num);
这段代码的意思就是从后往前拷贝这里加上一个num就代表来到了这个要拷贝目标的最后一个字节,和要拷贝对象的最后一个字节,这样来实现倒着拷贝。
以上就是对这两个函数的讲解了,如果有什么不足的欢迎指正。