void* memmove(void* dest, const void* src, size_t num)
返回值类型为void*,返回值为dest的起始位置,便于打印修改后的内容
功能是把 src~scr+num 中的内容拷贝到 dest~dest+num 中,虽然是move但实际上是cpy,还有一个参数是无符号整型,用于拷贝num个字节的内容,注意是字节不是元素个数
例如dest和src都是int类型,num=20,那么拷贝的是20/4 = 5个元素
实际使用:把arr2中前n个字节的内容拷贝到arr1中
memmove(arr1,arr2,n);
memmove的模拟实现:
void* my_memmove(void* dest, const void* src, size_t num) {
//标记修改的起始位置
void* tmp = dest;
assert(dest && src);
while (num--) {
//强转成char*,按字节修改
*(char*)dest = *(char*)src;
//分别指向下一字节
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return tmp;
}
但是,以上代码还有一个很严重的问题,就是当dest和src的距离小于num个字节的时候,如果src<dest,可能会出现把dest修改之后,src~src+num 中的内容也被修改
例如
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
此时src指向arr[0]=1,dest指向arr[2]=3
我们想修改从dest开始的前五个元素,使其变成 1 2 1 2 3 4 5 8 9 10
而实际上变成了 1 2 1 2 1 2 1 8 9 10
原因是此时src指向了被修改过的dest
arr[2]被改为1后,当scr指向arr[2]时,内容是1而不是原来的3了
所以当src+num<dest时,不应该从前往后修改,而是应该从后往前修改
先使src = src + num - 1,dest = dest + num - 1,分别指向末尾元素
这样第一次拷贝的结果为 1 2 3 4 5 6 5 8 9 10
此时src永远不可能指向被修改过后的dest
改进后的代码如下:
void* my_memmove(void* dest, const void* src, size_t num) {
//标记修改的起始位置
void* tmp = dest;
assert(dest && src);
//当目标地址小于源地址
//从前往后进行修改
if (dest < src) {
while (num--) {
//强转成char*,按字节修改
*(char*)dest = *(char*)src;
//分别指向下一字节
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
//当目标地址大于源地址
//从dest末尾开始修改
else {
分别指向各自的末尾
//dest = (char*)dest + num - 1;
//src = (char*)src + num - 1;
while (num--) {
//*(char*)dest = *(char*)src;
//dest = (char*)dest -1;
//src = (char*)src - 1;
//可以缩写成
*((char*)dest + num) = *((char*)src + num);
}
}
return tmp;
}