记录内存函数
我们上文讲了一个strcpy函数是字符串拷贝函数,从名字上我们就知道是专门针对字符串设计的函数,
如果我们想对一个整型数组或者浮点数数组进行拷贝那一定不能用strcpy函数,但是我们又想实现这一愿望,我们应该怎么办?
在C语言中,C语言给我们提供了一个库函数来帮助我们实现这个需求—memcpy
既然我们想使用memcpy函数,我们就得先去了解它;
void * memcpy ( void * destination, const void * source, size_t num );
我们可以发现memcpy函数的指针参数都是void类型,并没有具体明确是那个类型,这也就为我们使用该函数提供了极小的成本,void是可以接任何指针类型的,这也说明memcpy同时能对不同类型数组进行拷贝!!!参数和返回值介绍
功能:实现内存块的拷贝,以字节为单位,一个字节一个字节(注意不是以元素为单位!!!,是以字节!!!千万要记住!!!)的拷贝;
比如说:我有两个整型数组arr1和数组arr2现在arr1里面放的全是0,arr2里面放的是1,2,3,4,5,6……,然后现在呢我们通过memcpy函数就可以实现将arr2里面的数据拷贝到arr1里面去:
我们来使用看看:
测试代码:
int main(int argc, char* argv[]) {
int arr1[8] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8 };
memcpy(arr1, arr2, sizeof(arr2));
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(int); i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行截图:
既然我们知道了它的原理我们能不能对其进行模拟实现一下:
模拟代码如下:
void* my_memcpy(void* dest, const void* sour, size_t num)//模拟实现moncpy
{
assert(dest&&sour);
char* p1 = (char*)dest;
char* p2 = (char*)sour;
while (num--)
*p1++ = *p2++;
return dest;
}
运行截图:
同时能得到相同的结果
既然能实现两个数组的拷贝:那么能不能实现自己对自己的拷贝呢?
比如说我想要对arr2数组进行自我拷贝:我想从arr2[0]开始拷贝到arr2[2]拷贝5个元素,也就是拷贝20个字节:
我们能不能用memcpy实现呢?
我们先来分析:
首先如果能实现的话,我们的预期就是:1,2,1,2,3,4,5,8
现在我们来按照memcpy的思路来走一便,看看能不能到达预期:
很明显我们还是按照memcpy的思路去拷贝到话我们得不到预期的结果:
我们发现我们从源头区域的值有被修改过,就是我们有几次从源头区域读取的值会把后面源头区域的值给覆盖掉,造成我们从后面的源头区域读取时,读不到正确的源头数据的值,导致达不到预期效果,那么我们有什么办法取解决它呢?
既然我们从前往后读取会造成源头区域还没被使用就修改掉了,我们就从后往前读呗?什么个意思呢?
画个图:
我们可以发现是不是从后往前读取就符合预取结果了,但是是不是所有的数组都能像这样从后往前拷贝呢?
那如果现在我要求目标指针为arr2+0;源头指针为arr2+2,然后也是拷贝5个元素,如果我们也是按照从前拷贝能不能达到预期呢?(读者自行验证)
答案是不行的
想这种情况我们只能用从前往后读取的方法才能达到我们的预期;
由此我们可得,按照这样的分析,memcpy不能实现所有情况的自拷贝,只有当目标指针<=源头指针时memcpy才能实现自拷贝;目标指针>源头指针时memcpy不能实现自拷贝;于是为了实现全能的memcpy函数我们可以重新模拟一个memcpy函数
重新模拟memcpy函数:
void* my_momcpy(void* dest, const void* sour, size_t num)
{
assert(dest && sour);
char* p1 = (char*)dest;
char* p2 = (char*)sour;
if (dest <= sour)
{
while (num--)
{
*p1++ = *p2++;
}
}
else
{
while (num--)
{
*(p1 + num) = *(p2 + num);
}
}
return dest;
}
运行截图:
dest:arr2
sour:arr2+2;
dest:arr2+2;
sour:arr2;
这就模拟出了一个强大的memcpy
上面我们说到了库函数memcpy不能实现自拷贝,为了解决这个问题,我们重新实现了一个memcoy函数来解决这个问题,同时呢C语言也为我们提供了一个库函数来解决这个问题———memmove
void * memmove ( void * destination, const void * source, size_t num );
参数和返回值介绍:
功能:实现数组的自拷贝;
我们上面模拟的强大的memcpy函数就是对与memmove的模拟实现;
从名字上我们就能猜到其意义:内存比较函数(一个字节一个字节的比较)
与strcmp思想是一致的,strcmp是一个字符一个字符比较,memcmp是一个字节一个字节的比较(注意此时memcmp会把这一个字节里面的值当成无符号整型来看待(也即是当作无符号整型进行比较));
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
参数和返回值:
我们看一看其使用情况:
arr1第一个元素内存分布:01 00 00 00(16进制、小端)
arr2第一个元素内存分步:FF FF FF FF
比较第一个字节时:每个字节会被解释为无符号:01换为十进制就是:1
11换位10进制就是:255
255>1自然在mencmp看来arr2数组大于arr1数组;
模拟实现memcmp
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
assert(ptr1&&ptr2);
unsigned char* p1 = (unsigned char*)ptr1;
unsigned char* p2 = (unsigned char*)ptr2;
while (num&&(*p1==*p2))
{
p1++;
p2++;
num--;
if (!num)
return 0;
}
return *p1 - *p2;
}
内存设置函数,顾名思义,能对内存设置值,单位是每个字节:
void * memset ( void * ptr, int value, size_t num );
参数及其返回值:
同时我们需要注意尽管参数设计的时int(要设置的值),但是memset再将该值存入内存块的时候会被解释为无符号char类型才将值存入内存块,同时内存块(每个字节)也会被解释为无符号char类型
注意不是以元素为单位设置值,而是以字节为单位!!!
下面我们来实际应用一下:
注意我们不会打印1 1 1 1 ……
因为我们是对字节初始化!!!
比如arr[0]有四个字节则它内存分布:01 01 01 01
符号位为0,打印出来自然是16843009;
但是如果我们把每个字节要设置的值改为-1或0,那么整个数组才会是-1或0;
模拟实现一下memset
void* my_memset(void* ptr, int value, size_t num)
{
assert(ptr);
unsigned char a = value;
unsigned char* p = (unsigned char*)ptr;
while (num--)
{
*(p +num)= a;
}
return ptr;
}