一、memcpy的使用
memcpy是C语言的一个库函数,它与strcpy有相似之处,都是对一组数据进行剪切,但是memcpy可以对各种类型的数据进行剪切,而strcpy是字符串函数,它只能对字符串进行剪切。
使用memcpy之前我们可以先看一下它的参数和返回类型,
接下来我们对整形数组进行剪切,代码如下:
#include<stdio.h>
#include<string.h>
//memcpy的头文件
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 20);
//20为要剪切的字节数
for (int i = 0; i < 20; i++)
printf("%d ", arr2[i]);
return 0;
}
运行结果如下:
可以看到,我们剪切了20个字节,也就是5个整形。
二、memcpy的模拟实现
前面我们已经了解了mencpy的参数以及返回值,我们可以根据它来设计my_memcpy的参数以及放回值,下面看具体的代码实现:
#include<stdio.h>
#include<string.h>
//返回目标空间的起始地址
void* my_memcpy(void* p2, void* p1, size_t num)
{
void* ret = p2;
//由于我们要返回的值是目标空间的起始地址,因此我们需要先记录起始地址p2,
//因为p2在后面的运算中会发生改变,就不再是其实地址了
for (int i = 0; i < num; i++)
{
*(char*)p2 = *(char*)p1;
((char*)p1)++;
//强制类型转化是临时的,因此不能写成(char*)p1++,必须再来一个括号括起来
((char*)p2)++;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_memcpy(arr2, arr1, 20);
for (int i = 0; i < 20; i++)
printf("%d ", arr2[i]);
return 0;
}
其中,((char*)p2)++,((char*)p1)++也可以写成p2=(char*)p2+1; p1=(char*)p1+1;
三、memmove的使用
对于memcpy,有一个问题,那就是当需要剪切的内存空间发生重叠时,其结果可能是未定义的(当然也有可能是对的),如以下一段代码:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1 + 2, arr1, 20);
//在这里我们想要将1,2,3,4,5剪切到3,4,5,6,7中
//那么这里就发生了内存的重叠
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
在这段代码中,我们预期的结果是1 2 1 2 3 4 5 8 9 10,那么如果是要运行这段代码的话,结果可能是对的,也可能是错的,因此如果是要对有内存重叠的数据进行剪切的话,使用memmove函数
下面我们来看memove的使用:
可以发现,它的返回值与参数与memmove一模一样,其使用如下:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
运行结果:
可以看到,打印的结果与预期的完全相同。
四、memmove的模拟实现
在编写代码之前我们先分析以下,如下图:
我们的目的是将1,2,3,4,5剪切到3,4,5,6,7中,那么首先p1指向的值会赋值给p2指向的空间 ,然后p1,p2自增指向2和4,然后再进行赋值,那么我们发现,当我们想要将原来的5改成3是,3已经消失了,已近被改为了1,为了避免这种情况,我们可以从后往前开始交换,但是如果是将3,4,5,6,7,拷贝到1,2,3,4,5怎么办呢,这个时候我们可以从前往后交换,当然如果两组数据之间没有内存重叠的话,两种方法都适用,下面是代码的实现:
#include<stdio.h>
void* my_memmove(void* p2, void* p1, size_t num)
{
void* ret = p2;
//通过比较两个地址的大小来确定剪切方向
if (p2 < p1)
//前->后
//这种情况的思路和memcpy一样
{
for (int i = 0; i < num; i++)
{
*(char*)p2 = *(char*)p1;
((char*)p2)++;
((char*)p1)++;
}
}
else
//后->前
//我们可以再p1\p2强制类型转化为char*后加上19,刚好使
//p1,p2都指向最后一个数据
{
while (num--)
{
*((char*)p2 + num) = *((char*)p1 + num);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1+2, arr1, 20);
for (int i = 0; i < 10; i++)
printf("%d ", arr1[i]);
return 0;
}
运行结果如下,可以看到结果与预想的相一致,