参考链接:内存函数memcpy、memmove原理及模拟实现
strcpy和memcpy的区别
1.strcpy
/*
(1)函数原型
char *strcpy(char *dest, const char *src);
(2)功能
strcpy() 函数用来复制字符串;;strcpy 是依据 \0 作为结束判断的
(3)头文件
#include<string.h>
(4)返回值
成功执行后返回目标数组指针 dest。
(5)说明
注意:src 和 dest 所指的内存区域不能重叠,且dest 必须有足够的空间放置 src 所包含的字符串(包含结束符NULL)。
注意:如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出(buffer Overflow)的错误情况,在编写程序时请特别留意,或者用strncpy()来取代。
*/
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* sou)
{
assert(dest && sou);
char* ret = dest;
while (*sou != '\0')
{
*dest = *sou;
dest++;
sou++;
}
//*(dest++) = '\0';
return ret;
}
int main()
{
char arr1[] = "Hello World!";
char arr2[] = "****************************";
printf("%s", my_strcpy(arr2, arr1));
printf("\n");
return 0;
}
以上代码未放开注释“(dest++) = ‘\0’;”,输出结果为:
以上代码放开注释“(dest++) = ‘\0’;”,输出结果为:
2.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>
#include<assert.h>
void * my_memcpy(void *dest, const void* src, int n){
assert(dest != nullptr);
assert(src != nullptr);
assert(n > 0);
void * retr = dest;
//有多少个字节执行多少次
while(n--){
/*
只有强制转化为char*才可以实现一个字节一个字节进行赋值
另外(void*)不能直接解引用
*/
//一般我们用地址赋值都会先把地址解引用并赋值,然后让地址++。
/*
传参的地址都是void*类型的,当我们让地址++,地址不知道要跳几个字节。
比如:char* 类型的指针++,向后跳1个字节。而int* 类型的指针++,向后跳4个字节。
*/
/*
现在我们面临两个问题:
1.指针类型为void*,无法++。
2.要拷贝内容的个数不知道,通俗点就是我们不知道要拷贝几次或者两个指针要++几次。
对于第一个问题,我们把指针强制类型转换,这样就可以++了。但是如果让指针类型转换成int*话,我们就要循环count/sizeof(int)次,这样太麻烦我们还带确定用户要拷贝内容的类型。
对于这个问题我们可以把指针类型转换成char* ,因为char*类型指针++向后跳一个字节(一个字节一个字节访问),不管是什么类型我们都可以一个一个字节的拷贝,这样我们也容易确定循环次数,为count。
*/
*(char *)dest = *(char *)src;
dest = (char*)dest + 1;
src = (char *)src + 1;
}
//注意:我们也不要忘了,要拷贝一下目标地址的起始位置,因为函数要返回目标空间起始地址
return retr;
}
int main(){
int array1[] = {1,2,3,4,5,6,7};
int array2[20] = {0};
memcpy(array2, array1, 40);
for(int i = 0; i < 40; i++){
printf("%d ", array2[i]);
if((i + 1)%10 == 0)
printf("\n");
}
printf("\n");
return 0;
}
3.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<assert.h>
#include<stdio.h>
void * my_memmove(void * dest, const void * src, int n){
assert(dest != nullptr);
assert(src != nullptr);
assert(n > 0);
void *ret = dest;
if(dest < src || (dest > src + n)){正常情况下从前向后拷贝,和memcpy一样
while(n--)
{
*(char *)dest = *(char *)src;
dest = (char *)dest + 1;
src = (char *)src + 1;
//++(char *) dest;
//++(char *)src;
}
}else{
//有重叠
//当出现内存覆盖时从后向前拷贝
while(n--){
*((char *)dest + n) = *((char *) src + n);
}
}
return ret;
}
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;
}