1.memcpy使用和模拟实现
在前面我们学了字符串的拷贝函数strcpy但是我们不能只是拷贝字符串我们如果想要拷贝其他的怎么办呢?下面就来教学一个函数memcpy这个函数是针对内存块进行拷贝。
memcpy 函数是一个标准的 C 语言库函数,用于从源地址复制一定数量的字节到目标地址。这个函数通常用于内存操作,比如复制数组、结构体或其他数据块。
函数的原型是:
void * memcpy ( void * destination, const void * source, size_t num );
destination :指向目标内存区域的指针,复制的数据将被写入这里。
source :指向源内存区域的指针,数据将从这里复制。
num :要复制的字节数。
memcpy 函数返回一个指向目标内存区域的指针,即 destination 。
在使用这个函数的时候有注意事项:
下面还是老样子,如果不能理解也没有关系我会给一串代码,在里面进行解释:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
下面在进行这个函数的模拟实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include<assert.h>
//memcpy函数拷贝结束后,会返回目标空间的起始地址
void* my_memcpy(void* dst, const void* src, size_t num)
//实参都是int*但是这里的形参为什么是void*类型的指针?因为我们不只是想要传整型类型的,恰好void*类型的可以接受更多
{
void* ret = dst;//这里为什么要把dst的地址存起来?因为dst在下面要进行解引用然后不断地+1早已经改变原先的地址了,返回就不准确
assert(dst);
assert(src);
while (num--)//我们传入了20个字节,要一个字节一个字节的进行打印,所以我们要进行20次循环,
{
*(char*)dst = *(char*)src;//这里为什么要进行强制类型转换成char*类型的,在进行解引用呢?
//因为void*类型不能解引用而且只有char*类型可以一次访问一个字节
dst = (char*)dst + 1;
src = (char*)src + 1;
}
return(ret);//memcpy函数拷贝结束后,会返回目标空间的起始地址
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 20);//这里给的是20个字节一个整型占4个字节所以只能复制5个数字
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
这就是函数的模拟实现,我已经在里面进行详细的讲解。
但是下面的这句话是什么意思?
下面我再给串代码讲解一下: 这就是重合的情况。
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };//如果我们想把12345拷贝放到34567中怎么进行?
//就是这样arr1+2但是这样我们认为打印出来是这样的,下面这样。
// 1 2 1 2 3 4 5 8 9 10
//但是结果可不是这样的。
my_memcpy(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我们可以看出来我们想的和打印出来的就是不同,但是为什么呢?听我讲解:
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//当你打算把12345放大34567中的时候你先把1 2放到了3 4所在的地方,接下来会把3 4放到5 6上,但是这时3 4已经是1 2了,所以最终还是把1 2放到5 6中,7也是这个道理。
my_memcpy(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
但是这里还是有个小细节的 ,因为这是我们定义的my_memcpy函数如果使用memcpy函数是不是就不会有这种情况了?对的:
但是这串代码是对的,还是不能说明我们memcpy这个函数就是可以拷贝重叠情况,C语言标准库是规定不可以到但是这个函数还是做到了。并不冲突。
memcpy函数只用拷贝不重叠的情况,那重叠的情况怎么办?这就要用到memmove函数了。
2.memmove函数的使用和模拟实现
还是老样子我们来介绍一下什么是memmove函数
memmove 函数是 C 语言标准库中的一个函数,用于在内存中复制一块数据。它与 memcpy 函数类似,但 memmove 能够处理源和目标内存区域重叠的情况。当源和目标内存区域重叠时, memcpy 可能无法正确复制数据,而 memmove 能够确保数据的正确复制。
我们从这个定义中可以看出来这个函数的独特的地方。,可以处理重叠的情况。
下面我们来说一下这个函数的原型:
void * memmove ( void * destination, const void * source, size_t num );
dest :指向目标内存区域的指针。
src :指向源内存区域的指针。
n :要复制的字节数。
memmove 函数返回一个指向目标内存区域的指针。
原型其实是和memcopy函数大差不差的。那下面我们来看一下这个函数怎么处理重叠的情况
这里给出的还是上面的代码:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
就是这样,但是光看这个函数是不会了解本质的,我们可以进行模拟实现:
但是我们需要考虑一个问题我们怎么拷贝这个数字才能达到不会重叠拷贝的情况:
src<dest:假如还是一串数字1 2 3 4 5 6 7 8 9 10,我们要把 1 2 3 4 5拷贝到3 4 5 6 7里面这个时候我们就不能从前往后进行拷贝,那我们可以这样把5拷贝到7,把4拷贝到6,把3拷贝到5,把2拷贝到4,把1拷贝到3,就像这样,把要拷贝数字从后往前进行拷贝。
但是呢还是有一种情况我们还需要注意:如果我们想要把3 4 5 6 7拷贝到1 2 3 4 5这时候怎么办呢?
dest<src:那我们可以这样把3拷贝到1,把4拷贝到2,把5拷贝到3,把6拷贝到4,把7拷贝到5,就像这样,把要拷贝数字从前往后进行拷贝。
重要:对于这两种情况我们可以进行总结:如果我想拷贝的一串数字在目标数字前(src<dest:)我就把想拷贝的数字从后往前一个个的拷贝到目标数字里面去;如果是在后面(dest<src)的话就要从前往进行拷贝。
那接下来我们把函数模拟实现出来:那么这里我先给一个大体的框架:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
if (dest < src)
{
//前—>后
}
else
{
//后-->前
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
就是这样在接下来就开始进行细致的讲解:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
//前—>后
while (num--)//从后往前拷贝的还是和之前一样。
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后-->前
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
//这里是什么意思?因为这是从后往前进行拷贝的,把dest和src强制类型转换成char*类型之后是一个字节
// 当我们再加上num,就是加上19个字节最后就成为了20个字节,就是要拷贝的第五个数字的最后一个字节
//那这里为什么是19因为num在进来的时候就 -- 了
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
总结:这就是这两个函数的区别我们可以了解一下。