hello大家好,我是阿乐。进来我们主要说说两个内存函数memcpy、memmove的原理以及模拟实现,希望各位看官老爷们能有所收获有所帮助!同样我也在不断学习和改进当中,有什么问题欢迎大家评论区留言或者私信哦。
话不多说,让我们直接进入正题!
目录
一、memcpy函数
1.memcpy函数介绍及实现
(1)函数原型
void *memcpy(void*dest, const void *src, size_t n);
(2)功能
由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。
(3)头文件
#include<string.h>
(4)返回值
函数返回一个指向dest的指针。
(5)说明
1.source和destin所指内存区域不能重叠,函数返回指向destin的指针。
2.与strcpy相比,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。
memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;
例:
char x[100], y[50];
memcpy(y, x,sizeof(y)); //注意如用sizeof(x),会造成y的内存地址溢出。
而strcpy就只能拷贝字符串了,它遇到'\0'就结束拷贝;
例:
char a[100], b[50];
strcpy(a,b);
3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
注意:source和destin都不一定是数组,任意的可读写的空间均可。
例子:
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
//将arr1中的内容,拷贝到arr2中
memcpy(arr2, arr1, 40);
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
代码调试结果如下:
2.memcpy函数的模拟实现
我们直接上代码,再来一步一步解读。
void* my_memcpy(void* det, const void* src, int num)
{
void* ret = det;
while (num)
{
*(char*)det = *(char*)src;
det = (char*)det + 1;
src = (char*)src + 1;
num--;
}
return ret;
}
int main()
{
float arr1[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
float arr2[10];
int i = 0;
int len = sizeof(arr1);
my_memcpy(arr2, arr1, len);
for (i = 0; i < 9; i++)
{
printf("%0.8lf ", arr2[i]);
}
return 0;
}
(1)首先,是给我们模拟的函数起个名为my_memcpy。这样方便我们理解的同时也不失为一个好听的名字~
(2)然后就是根据memcpy函数的参数来给我们的my_memcpy函数对应起来,模仿的函数原型如下
void* my_memcpy(void* det, const void* src, int num)
(3)接下来就是具体实现我们的my_memcpy函数了。
一般我们用地址赋值都会先把地址解引用并赋值,然后让地址++。
*dest = *src;
dest++;
src++;
可是,在模拟函数中,传参的地址都是void*类型的,当我们让地址++,地址不知道要跳几个字节。
比如:char* 类型的指针++,向后跳1个字节。而int* 类型的指针++,向后跳4个字节。
现在我们面临两个问题:
1.指针类型为void*,无法++。
2.要拷贝内容的个数不知道,通俗点就是我们不知道要拷贝几次或者两个指针要++几次。
对于第一个问题,我们把指针强制类型转换,这样就可以++了。但是如果让指针类型转换成int*话,我们就要循环count/sizeof(int)次,这样太麻烦我们还带确定用户要拷贝内容的类型。
对于这个问题我们可以把指针类型转换成char* ,因为char*类型指针++向后跳一个字节(一个字节一个字节访问),不管是什么类型我们都可以一个一个字节的拷贝,这样我们也容易确定循环次数,为count。
注意:我们也不要忘了,要拷贝一下目标地址的起始位置,因为函数要返回目标空间起始地址
二、memmove函数
1.memmove函数介绍及实现
(1)函数原型:
void *memmove(void *str1, const void *str2, size_t n)
(2)功能:
从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。
(3)头文件:
#include <string.h>
(4)返回值:
该函数返回一个指向目标存储区 str1 的指针。
(5)说明:
memcpy()函数如果遇到内存重叠,就无法得到理想的拷贝。而memmove()函数就恰恰解决了这一问题。
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "memmove can be very useful......";
memmove(str + 20, str + 15, 11);
puts(str);
return 0;
}
代码调试结果如下:
2.memmove函数的模拟实现
还是和上面一样,先上代码。
void* my_memmove(void* dest, const void* src, int n)
{
char* pdest = (char*)dest;
const char* psrc = (const char*)src;
assert(dest);
assert(src);
if (pdest <= psrc && pdest >= psrc + n)//正常情况下从前向后拷贝
{
while (n--)
{
*pdest = *psrc;
}
}
else //当出现内存覆盖时从后向前拷贝
{
while (n--)
{
*(pdest + n) = *(psrc + n);
}
}
return dest;
}
int main()
{
char arr[10] = "abcdefg";
char arr0[10] = "abcdefg";
char arr1[10] = { 0 };
my_memmove(arr + 2, arr, 4);
my_memmove(arr1, arr0, 4);
printf("%s\n", arr);
printf("%s\n", arr0);
printf("%s\n", arr1);
return 0;
}
首先 memmove 是一个内存操作函数,不是字符串操作函数,它可以处理多种类型的数据。
它的原型是:void *memmove( void* dest, const void* src, size_t count );
这里有两个点需要记住:
1、它的返回值是void*,参数类型也是void*,这样它才可以处理不同类型的数据。
2、目标dest不能加const,源src加const。原因是我们需要从源src中拷贝数据到dest中,需要对dest进行赋值。若用const保护dest,便不能完成赋值操作。
memmove的好处是可以处理dest与src发生内存重叠的情况。
今天的内容到这里就结束啦,希望以上内容对你们能有所帮助!