一、memcpy 函数
数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。注意是以字节为单位进行拷贝。函数声明如下:
1、参数返回值解析
第二个参数 src:源地址,即你要从哪开始拷贝。
第三个参数 count:字节数,即从源地址起始位置开始一共要拷贝多少个字节。
第一个参数 dst:目的地址。也就是你拷贝的内容要放到哪。
返回值:返回目标空间的地址。
2、使用注意
该函数有如下两个注意事项:
- 因为是内存函数,并不会以' \0 '作为结束符。
- 不适合用于处理内存重叠的情况。举个例子,将源地址的第1 ~ 4个字节拷贝到源地址的第3 ~ 6的字节位置上,此时发生了内存重叠。(如果有内存重叠,则需要使用memmove函数)
3、模拟实现
因为是逐字节拷贝,所以解引用的时候,每次访问只能访问一个字节,前面在说指针类型的时候提到,指针类型决定了解引用每次可以访问多少个字节。
因此,在拷贝之前,先将源地址和目标地址的指针类型转化为 char* 类型,这样的话,每次解引用就只会访问一个字节。
#include <assert.h>
#include <stdio.h>
void* my_memcpy(void* dst, const void* src, size_t num)
{
assert(dst && src);
char* target = (char*)dst; // 转化为 char*,解引用时只会访问一个字节
const char* source = (const char*)src;
while (num--)
{
*target = *source;
target++;
source++;
}
return dst;
}
int main() {
int dst[10] = { 0 };
int src[5] = { 1,2,3,4,5 };
my_memcpy(dst, src, sizeof(src));
for (size_t i = 0; i < 10; i++)
{
printf("%d ", dst[i]);
}
return 0;
}
测试结果如下:
二、memmove 函数
1、认识memmove函数
当源地址和目标地址没有重叠的时候,memmove函数 和 memcpy函数的功能是一样的,但是如果遇到内存重叠的情况,比如将源地址的第1 ~ 4个字节拷贝到源地址的第3 ~ 6的字节位置上。
函数声明如下, 参数解析可以参考 memcpy 函数
2、memmove函数模拟实现
在模拟实现的时候,需要考虑的是,源地址和目的地址的位置。如果源地址在目的地址的左边,此时应该先将末尾的字节拷贝到目标地址的末尾。如果按正常顺序拷贝的话,就会变成右边这种情况,明明 3 也是要拷贝到目标地址的,但是现在却被新拷贝的覆盖了。
如果 边,应该是从后往前拷贝。这个时候相当于是把蓝色框里的内容向后移动了一样,最先移动的是最右边的内容。
如果源地址在目的地址的右边,那么这个时候,就应该是从前往后拷贝。这个时候就相当于是把蓝色框里的内容向前移动了一样,最先移动的是最左边的内容。
void* my_memmove(void* dst, const void* src, size_t num)
{
assert(dst && src);
char* target = (char*)dst;
const char* source = (const char*)src;
if (src < dst)
{
// 源地址在目的地址的左边
while (num--)
{
*(target + num) = *(source + num);
}
}
else if (src > dst)
{
// 源地址在目的地址的右边
while (num--)
{
*target = *source;
target++;
source++;
}
}
else
{
// 无需拷贝,直接返回
return (void*)src;
}
}
int main() {
int arr[8] = { 1,2,3,4,5,6,7,8 };
my_memmove(arr, arr + 2 , 4 * sizeof(int)); //移动4个元素(16个字节)
for (size_t i = 0; i < 8; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
测试结果为: