目录
前言:
我可以通过学习的难易顺序来分,可以说 strcpy函数 是最简单的复制(拷贝)函数,但是其只能运用于字符串,随后就是 memcpy函数 ,其实基于内存上的复制(拷贝)函数,相较于 strcpy函数 不会具有同样的局限性,但是 memcpy函数也是有其的巨大缺陷,其不能对本身进行覆盖拷贝,于是便有了 memmove函数 ,其是能够对本身进行覆盖拷贝的函数,其又同时兼备了 memcpy函数 可做的事。(注:保留 memcpy函数 是因为保证用memcpy函数进行书写的代码的顺利通过性,所以即使 memmove函数 玩玩全全强于 memcpy函数 也是不能将其删去,于是也会有这样的两个函数,但是在有些编译器下,也将 memcpy函数 实现成为与 memmove函数 同样的功能(例如:vs2019)。)
对本身进行覆盖拷贝:
#include <stdio.h>
#include <string.h>
void print(int* arr,int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 1, arr + 3, 20);
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
printf("\n");
return 0;
}
1 . 介绍 memmove函数
函数原型:void *memmove(void *dest, const void *source, size_t count)
返回值说明:返回指向dest的void *指针
参数说明:dest,src分别为目标串和源串的首地址。count为要移动的字节的个数
函数说明:memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。
2 . 模拟 memmove函数
2 . 1 . 简易模拟(不考虑覆盖拷贝)
首先,创建这个函数的作者是无法考虑到如今我们需要怎么样的类型进行拷贝,于是,同样的我们就需要用到 void* 类型的指针,来进行函数传参的接收。
此处我们需要注意:
- 防止dest与src是空指针,所以进行assert;
- 我们此处无需改变src地址的函数,所以以const经进行保护。(原memmove也是如此书写的)
- 返回指向dest的void *指针,需要将地址先行存下,因为后面会发生变化。
- 进行count次,所以利用while循环即可。
- (类型名)是临时类型转换。
- ++(char*)dest等类似的减少书写,在有些编译器下可能会导致无法通过,采取的dest = (char*)dest + 1可以保证通过。
#include <stdio.h>
#include <assert.h>
//memmove函数的模拟
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(dest && src);
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
//数组的打印
void print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memmove(arr1, arr2, 20);//memmove函数的模拟
int sz = sizeof(arr1) / sizeof(arr1[0]);
print(arr1, sz);//数组的打印
printf("\n");
return 0;
}
2 . 2 . 模拟分析(考虑覆盖拷贝)
2 . 2 . 1 覆盖拷贝所在的问题
此处,我们就要重点注意覆盖拷贝的问题,因为会导致未被拷贝的数值已招到更改。以图为例:
就以之前所模拟(简易模拟(不考虑覆盖拷贝))实验就可以发现,只需将:
就可以得到:
2 . 2 . 2 覆盖拷贝所在的问题
本人想到的就只有:
- 重新开辟一个空间,便于原数据的储存。
- 以不同的角度,以逆序拷贝or顺序拷贝以保证未进行拷贝的数据,保持原样。
方法一:会有一个严重的问题,根本不知道需要开辟多大的空间(本人使用vs2019)。小了,会出现程序错误;大了,会导致空间浪费。所以在此看来,这样的不行的。
方法二:在针对不同的情形下,只要考虑完全,这就是很完美的选择,同时也不会浪费空间。
2 . 2 . 3 覆盖拷贝的书写
我们所知晓的覆盖错误就是因为,未进行拷贝就被更改,那我们就想办法,在其未被修改前进行拷贝。
情况一:src在dest之前。
我们可以发现自src下标小端拷贝,就不会出现之前的未进行拷贝就被更改的情况。(逆顺序)
情况二:dest在src之前。
我们可以发现自src下标大端拷贝,就不会出现之前的未进行拷贝就被更改的情况。(顺顺序)
而在未出现覆盖的情况,怎么样的顺序的拷贝都是可以实现的。于是,就可以正式开始书写代码了。
2 . 3 . 模拟书写(考虑覆盖拷贝)
2 . 3 . 模拟细节(考虑覆盖拷贝)
经过之前的分析,因为未被覆盖的类型,任何顺序都是可以的。于是,为了书写代码的简洁,我们将 src < dest 全分为逆顺序, src > dest 全分为顺顺序。(数组是随着下标的增长,地址随之变大)
src < dest :
//逆顺序
while (count--)
{
*((char*)dest+count) = *((char*)src + count);
}
src < dest :
//顺顺序
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
2 . 3 . 全模拟代码(考虑覆盖拷贝)
void* my_memmove(void* dest, const void* src, int count)
{
assert(dest && src);
void* ret = dest;
if (src > dest)
{
//顺顺序
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//逆顺序
while (count--)
{
*((char*)dest+count) = *((char*)src + count);
}
}
return ret;
}
2 . 3 . 全模拟代码举例(考虑覆盖拷贝)
#include <stdio.h>
#include <assert.h>
//memmove函数模拟
void* my_memmove(void* dest, const void* src, int count)
{
assert(dest && src);
void* ret = dest;
if (src > dest)
{
//顺顺序
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//逆顺序
while (count--)
{
*((char*)dest+count) = *((char*)src + count);
}
}
return ret;
}
//数组的打印
void print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 3, arr + 1, 20);
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);//数组的打印
printf("\n");
return 0;
}