语法讲解
C语言中的`memmove`函数是一个非常有用的内存操作函数,它可以将一段内存内容从一个位置移动到另一个位置,即使在源内存和目标内存有重叠的情况下,它也能正确地完成内存复制工作。
函数原型定义如下:
```c
void *memmove(void *dest, const void *source, size_t n);
```
参数说明:
- `dest`:指向目标内存的指针,这里将复制数据到这个地址空间。
- `source`:指向源内存的指针,这里是要复制的数据所在的位置。
- `n`:要复制的字节数。
返回值:
- 函数成功执行后,返回指向`dest`的指针。
- 如果参数指向的内存区域非法,则返回`NULL`。
`memmove`的工作原理是,如果源地址和目标地址是不重叠的,那么它就像`memcpy`函数一样工作,直接复制数据。如果源地址和目标地址重叠,`memmove`会采取一种高效的方法,从后向前复制数据,以避免在复制过程中覆盖数据。
使用示例:
```c
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello, World!";
char dest[20];
// 确保目标数组有足够的空间来存放源字符串
strcpy(dest, source);
// 使用memmove将source字符串移动到dest数组的前面
memmove(dest, source, strlen(source) + 1);
printf("Copied string: %s\n", dest);
return 0;
}
```
在上面的示例中,我们首先用`strcpy`将字符串复制到`dest`数组,然后使用`memmove`将`source`数组的内容移动到`dest`数组的前面。注意,这里我们使用了`strlen`函数来获取字符串长度,并加上1来确保还包括字符串结束符`\0`。
需要注意的是,在使用`memmove`时,要确保移动操作不会导致内存访问越界,特别是在处理动态分配的内存或者指针运算时要格外小心。
memcpy和memmove的区别
memcpy的逻辑
memmove的逻辑
这个函数和memcpy其实是差不多的 ,本质上都是拷贝函数地址从而达到目的,但是需要知道的是memcpy是不能完成重复函数的拷贝的 什么意思呢 简单的说就是,重叠就交给memove不成重叠交给memcpy。
第一种情况 把同一个数组里面的 1 2 3 拷贝到2 3 4 里面去这个时候就会产生问题 也就是
我们需要的是 1 1 2 3 5 6 7 8
但是实际我们得到的可能是 1 1 1 1 5 6 7 8
也就是1 拷贝给2,2 变成1,2->1拷贝给3,此时3也变成1,但是实际我们需要的是1 1 2 3 5 6 7 8所以是不能满足目的的,如果有些编译器可以满足拷贝要求 ,那更大的可能就是,memcpy和memmove函数一样。
也就是
1
2
图解(原理讲解)
也就是此时存在两种情况,一种是被拷贝空间在拷贝空间前面,一种是被拷贝空间在拷贝空间后面, 这个时候我们提出两种解决方式,也就是一种是从前向后拷贝,一种是从后向前拷贝
1,需要把前面的空间拷贝到后面去,并且不能覆盖和重复
当被拷贝函数在前面的时候,我们分析两种图解
第一种是从前向后拷贝(这里的从前向后拷贝是第一个拷贝到最后一个)(不满足)
第二种是从后向前拷贝(这里的从后向前拷贝是被拷贝函数的从最后一个元素拷贝到第一个元素)(满足)
2当被拷贝函数在后面的时候,我们分析两种图解
这里也是两种情况
第一种从前往后拷贝(满足)
第二种从后往前拷贝 不满足
画图总结
地址是从低地址到高地址
也就是
低----------------------------------------->高
拷贝重复地址的模拟实现
这个不像memcpy 拷贝不重复地址 memcpy就算完成任务 也是超额完成
这里的完成任务不算超额完成
5*sizeof(int) 这里是20个字节大小
这里是 sizeof(int)四个字节 这里传递的是字节
模拟函数
图解
把1 2 3 4 5 拷贝到 3 4 5 6 7
版本1(不是最优解)
版本2(最优解)
忘记知识是一个客观事实 但是思考方式是不会忘记的
地址从低地址到高地址
第二个是从前到后
第一个是从后到前
所以
代码总结
#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
void* my_memmove(void* dest,const void* src, size_t num)
{
assert(dest && src);
if (src > dest)
{
while (num--)
{
*(char*)dest = *(char*)src;
src = (char*)src + 1;
dest = (char*)dest + 1;
}
}
else
{
while (num--)
{
//这里可以这样理解 也就是 本质还是每次许循环都是相等的
//但是因为是从后向前拷贝的 所以此时是
// 当 scr指向8 的时候 他的最后数值的时候
// dest刚好指向的也是最后一个数值
// 因为是首元素地址+num 这个num是20个字节 也就是武哥元素 也就是同时他们指向的都是各自需要交换的最后一个字节
//最后再每次进行-- 直到循环结束 此时结束循环
*((char*)dest + num) = *((char*)src + num);
}
}
}
void* MY_memmove(void* dest, const void* scr, size_t num)
{
assert(dest && scr);
//首先我们需要知道 地址是从低地址到高地址进行的
// 也就是我们需要知道 什么时候scr 小于dest 什么时候大于dest 根据这个计算 就可以得到想要的结果
// 我们需要知道 scr 是被拷贝的空间 dest 是拷贝的空间
if (scr > dest)
{
while (num--) //这里还是每次减少一个字节 这里是被拷贝空间在拷贝空间后面 所以其实也发现并没有什么变化 那么也是可以把memcpy的函数直接拿过来
{
*(char*)dest = *(char*)scr;
scr = (char*)scr + 1;
dest = (char*)dest + 1;
}
}
else//这里包含小于等于 也可以不包含等于 这里是scr<dest也是就需要从后往前进行打印
{
while (num--)
{
*((char*)dest + num) = *((char*)scr + num);//这里的意思是
}
}
}
int main()
{
char arr1[] = "dfskbjh ";
char arr2[100] = { 0 };
memmove(arr2, arr1, 7);
printf("%s \n", arr2);
char arr3[] = "dfskbjh ";
char arr4[100] = { 0 };
memmove(arr4, arr3, 5 * sizeof(char));
printf("%s \n", arr4);
int arr5[] = { 1,2,3,4,5,6,7,8,9,1 };
int arr6[100] = { 0 };
memmove(arr6, arr5, 20);//这里是把不重复地址复制到arr6里面 20个字节也就四个数值
for (int i = 0; i < 20; i++)
{
printf("%d ", arr6[i]);
}
printf("\n");
int arr7[] = { 1,2,3,4,5,6,7,8,9,1 };
memmove(arr7, arr7 + 5, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr7[i]);//这里是直接打印数组
}
printf("\n");
int arr8[] = { 1,2,3,4,5,6,7,8,9,1 };
memmove(arr8 + 2, arr8, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr7[i]);//这里是直接打印数组
}
printf("\n\n\n");
int arr9[] = { 1,2,3,4,5,6,7,8,9,1 };
my_memmove(arr9 + 2, arr9, 12);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr9[i]);//这里是直接打印数组
}
printf("\n\n\n");
int str1[] = { 1,2,3,4,5,6,7,8,9,1 };
MY_memmove(str1, str1+2, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", str1[i]);
}
printf("\n\n\n");
return 0;
}