1,memcpy
1.1memcpy介绍和演示
memcpy 是 C 语言中的一个标准库函数,用于从源内存块复制指定数量的字节到目标内存块。这个函数的原型通常定义在 <string.h> 头文件中。
函数原型:
void *memcpy(void *dest, const void *src, size_t n);
参数一dest:指向目标内存块的指针,即要复制到的位置。
参数二src:指向源内存块的指针,即要复制的数据所在的位置。
参数三n:要复制的字节数。
返回值:返回指向目标内存块 dest 的指针。
memcpy是将内存逐个字节复制,操作对象是内存,所以两个参数都是用void*类型,可以接收任意类型的指针。
函数演示:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[20];
memcpy(dest, src, 5); // 将 src 的前 5 个字节复制到 dest
dest[5] = '\0'; // 确保字符串正确终止
printf("Copied string: %s\n", dest);
return 0;
}
因为memcpy只会复制对应字节数的内存,所以我们还需自己增加一个’\0’确保字符串正常结尾。
结果为:
使用memcpy有几个注意事项,
一:memcpy 不会检查目标内存块是否足够大以容纳要复制的数据。如果目标内存块太小,会导致缓冲区溢出,
二:memcpy 不会检查源内存块和目标内存块是否重叠。如果两个内存块重叠,应使用 memmove 函数代替。
1.2memcpy模拟实现
因为memcpy函数的操作对象为内存块,所以memcpy应该对任意类型的数据都能进行复制,所以我们在模拟实现时要使用void*类型来接收参数,同时会返回一个指向目标内存的指针
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest,const void* src,size_t num)
{
void* ret = dest;//记录目标地址
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
}//char*类型的指针解引用访问一个字节,逐个字节复制
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = {0};
my_memcpy(arr2, arr1, 20);
for (int i = 0;i < 10;i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
结果为
2,memmove
2.1memmove介绍和演示
在上面介绍memcpy函数时提到过,当复制的源内存块和目标内存块重叠时,不能使用memcpy,这时想正确地进行复制操作,应该使用另一个函数memmove。
memmove 是 C 语言中的一个标准库函数,用于从源内存块复制指定数量的字节到目标内存块,即使源内存块和目标内存块重叠,也能正确地进行复制操作。这个函数通常定义在 <string.h> 头文件中。
函数原型:
void *memmove(void *dest, const void *src, size_t n);
该函数的三个参数与返回值和memcpy没有区别,不同的是,在使用memmove进行复制内存时,函数会判断内存位置,然后正确的复制内存内容。
函数演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello, World!";
memmove(str + 2, str, 5);
printf("Moved string: %s\n", str);
return 0;
}
结果为:
在这个示例中,memmove 函数将 str 的前 5 个字节(即 “Hello”)移动到了 str 的第 3 个位置开始处,因此字符串变为 “lllo, World!”。如果使用 memcpy 来完成这个操作,结果将是不确定的,因为源内存块和目标内存块重叠了。那memmove是如何完成复制的呢?我们分析可以得知,当我们从第一个字节开始逐个向后复制内存,复制了2个字节后字符串变为“HeHeo,World!”,此时在复制第三个字节的内存,字符串就将变为==“HeHeH,World!”==是错误的,这是因为前面的内容已经被覆盖了,
因此,当遇到这种情况时,我们可以选择从后向前复制,分析可得当复制2个字节内存后,字符串内容变为”Helloloorld!“,若从后向前复制,此时的src指针指向字符”l“,dest指针指向o,继续向后复制,能得到我们想要的结果。因此,memmve会跟据指针的位置判断是从前向后,还是从后向前复制内存。从而做到在复制的源内存块和目标内存块重叠时,正确的复制内存块。
2.2memmove模拟实现
综上所述,我们在模拟实现memmove函数时,最主要的就是理清,memmove函数什么时候从前向后复制内存块内容,什么时候从后向前复制内存块内容。我们可以画图方便理解:
经过分析,我们可以知道,当dest指针指向的地址在src指针指向的地址之前时,我们可以从前向后复制内存块,而当dest指针指向的地址在src指针指向的地址之后时,我们可以从后向前复制内存块。
也就是说,我们在模拟实现memmove函数时,得先判断两指针指向的地址大小。
#include<stdio.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
if (dest < src)//dest指针指向的地址在src指针指向的地址之前
{
while (num--)
{
*(char*)dest = *(char*)src;
dest =(char*)dest+1;
src =(char*)src+1;
}
}
else//dest指针指向的地址在src指针指向的地址之后
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memmove(arr1+2, arr1, 5 * sizeof(int));
for (int i = 0;i < 10;i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
两种结果
结果一:
结果二: