1.memcpy函数使用和模拟实现
2.memmove使用和模拟实现
3.memset函数的使用
4.memcmp函数的使用
(使用这些函数需要引用头文件 string.h)
1.memcpy使用和模拟
1.void* memcpy(void* destination,const void* source,size_t num)
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
2.这个函数在遇到 '\0' 的时候并不会停下来(与strcpy函数不同)。
3.如果source和destination有任何的重叠,复制的结果是未定义的(达不到预期)。
int main()
{
int arr1[] = { 0,1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 20);
//printf("%d", arr2);
for (int i = 0; i < 10; i++)
{
printf("%d", arr2[i]);
}
return 0;
}
下面是目标位置与来源位置有重叠的情况:
目标位置在数组arr1 2(元素)的位置上 ,来源位置在数组的起始位置,复制的大小是五个字节,有重复的位置,按照原来的功能是应该按下图来的,在第二次的时候sre指向已经被覆盖的dest原位置,此时会把已经覆盖的数据作为来源数据,当来源数据应该是2和3,所以结果是不符合的,没有的原因是函数优化了(实现功能与下面的memmove基本无差)。
接下来是如何模拟memcpy函数:
#include<stdio.h>
#include<stdlib.h>
void* my_memcpy(void* dest, const void* sre, size_t num)
{
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)sre;
dest = (char*)dest + 1;
sre = (char*)sre + 1;
}
return ret;
}
int main()
{
int arr1[] = { 0,1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_memcpy(arr2, arr1, 20);
//printf("%d", arr2);
for (int i = 0; i < 10; i++)
{
printf("%d", arr2[i]);
}
return 0;
}
my_memcpy需要的参数是俩个void型的指针,因为不确定是什么类型的地址传过来,num是复制的字节大小,第一步是先用临时变量来存储dest的位置,因为后续的操作会改变dest的起始位置,
而函数的返回值是一个地址的起始位置,把dest和sre强制转化的原因是num是字节大小,使用变为char*类型刚好是一个字节大小,通过一个一个字节的赋值,直到把num个字节大小都给赋值,最后在返回临时变量ret(起始位置)。
用模拟的函数可以看到重叠时的效果(未定义的)
函数库里的memcpy原本应该是这个效果。
2.memmove使用和模拟实现
1 void* memmove(void* destination,const void* source,size_t num);
1.和memcpy的差别就是memmove函数处理的原内存块和目标内存块是可以重叠的。
2.如果原空间和目标空间出现折叠,就要使用memmove函数去处理。
memmove与memcpy定义的区别是重叠问题的处理,但是通过上面可以知道,其实俩者没什么区别,能实现一样的效果。
接下来是memmove函数的实现:
#include<stdio.h>
#include<stdlib.h>
void* my_memmove(void* dest, const void* sre, size_t num)
{
void* ret = dest;
if (dest < sre)
{
while (num--)//从前往后
{
*(char*)dest = *(char*)sre;
((char*)dest)++;
//括号是一定有的,(char*)dest++是不行的,强制转化临时的,转化之后没有使用,++之后已经不是强制转化之后的结果了。
((char*)sre)++;
}
}
else
{
while (num--)//从后往前
{
*((char*)dest + num) = *((char*)sre + num);
//num进来会-1,最后面的位置是起始位置+覆盖的字节数量-1,所以强制转化的dest+num刚好为最后面位置。
}
}
return ret;//memmove返回值是目标空间的起始位置
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_memmove(arr1 + 2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d", arr1[i]);
}
return 0;
}
对于重叠会有三种情况:
第一种情况:
当dest的位置比sre的位置大,此时若按照从前往后覆盖会出现上面memcpy函数实现中重叠的情况,所以需要从后往前覆盖,这样可以保证来源数据不会被覆盖改变(sre)。
第二种情况:
当dest的位置小于sre位置,此时就不能从后往前的覆盖,会改变来源数据的值,需要从前往后的覆盖。
第三种情况:
就是不重叠的情况,这时候用第一种或者第二种的方法都可以实行。
(模拟的)
3.memset函数的使用
1 void* memset(void* ptr,int value,size_t num);
1.memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
从起始位置开始往后四个字节大小设置成‘x’,size_t num是以字节为单位的。
上图可知num字节单位,memset是对每个字节进行处理,int有四个字节,所以只会处理第一个字节,并不是处理这个int大小。
所以要谨慎使用memset函数来赋值。
4.memcmp函数的使用
1 int memcmp(const void * ptr1,const void* ptr2,size_t num);
1.比较从ptr1和ptr2指针指向的位置开始,先后的num个字节
2.返回值如下:
memcmp函数是对比num个字节大小的数据,16字节对于四个整型,就是对比前四个数据的大小,一样返回0。
而当对比五个字节大小时, 前四行一样,第五行时 05大于04,所以第二个地址大于第一个地址,返回-1.(存储是小端模式存储)。