一、memcpy memmove memcmp的介绍、用法以及注意事项
二、具体实现memcpy memmove memcmp
一、memcpy memmove memcmp介绍、用法以及注意事项
1.1介绍
形如memcpy memmove memcmp的这些函数我们都叫它成员函数,它不被数据的类型所局限,
可以对任意类型的数据进行copy,move,compare的操作。
1.2为什么要有成员函数
在c语言内置的库函数中,已经有了strcpy strcmp strstr等函数,为什么还要有成员函数呢?
主要原因是strcpy strcmp 和strstr函数主要针对字符串进行操作,无法实现对其他数据类型的操作。
1.3如何使用成员函数
我们先在MSDN中查看一下这三个函数的具体参数和返回类型,后面具体实现时不多赘述。
memcpy是对成员进行复制,memmove实际上也是进行复制,但它在特定情况下能处理memcpy所不能解决的问题,memcmp是对成员进行比较。
三个函数的参数都是一样的,分别是void*dest,void*src,size_t count。第一个参数是要执行操作的目标地址,第二个参数就是提供操作数据的源地址,第三个参数是需要进行操作的字节数。
这说明了这个函数的操作是对数据逐字节的,为了验证这个事实,我用memcmp函数举一个例子。
1.4使用的注意事项。
请看下面一段代码,
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[] = { 1,0x00001102,5 };
int arr2[] = { 1,3,5 };
int ret = memcmp(arr, arr2,8);
printf("%d", ret);
return 0;
}
memcmp函数如果在比较每个字节的数据时,找到了比另外一个大的,就返回大于0的数,反之返回小于0的,相等就返回0。arr1数组中的第二个元素0x00001102是一个以16进制表示的数字,在当前机器下是小端存储的方式,故它在内存中应以02 22 11 00的方式存放。
arr2数组中对应位置元素为3,它在内存中的存储方式为 03 00 00 00,
从而如果真的是以字节的大小比较的话,第二个字节位的11比00来的大,从而ret应该是一个大于0的数字,我们来看一下结果。
说明memcmp函数确实以字节的形式比较, 其他的函数也是一样,这里就不再举例了。
memcpy函数无法实现对自身的复制,比如。
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = {1,2,4,5,6};
memcpy(arr+1,arr,12);
for(i = 0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d",arr[i]);
}
return 0;
}
在某些机器下的运行可能会出现1 1 1 1 6的情况,并不是我们想象中的1,1,2,4,6的情况,也就是memcpy函数无法对自己进行复制操作,像这种目标操作空间和源操作空间有重叠的情况,我们一般用memmove来操作。memcpy的这个缺陷在vs2019下已经得到了优化,是因为vs已经把memcpy实现成和memmove同样的代码。
还有,memcpy遇到‘\0’并不会停下来。
二、memcpy、memmove、memcmp的实现方式
2.1memcpy的实现
#include<assert.h>
void my_memcpy(void* dest, void* src, size_t num)
{
assert(dest && num);
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
首先防止指针参数为空,用assert进行断言。
由于三个函数都是对字节进行操作的,并且他们并不知道传过来的是什么参数,所以需要把两个指针参数强转为char*类型,才能进行指针的运算,只要num非0,我们就一直把数据的每个字节进行交换。
2.2 memmove的实现
假设dest和src的起始位置如上图所示,如果要复制12个字节的数据,那么他们的空间出现了重叠。如果按顺序进行交换,那么第一个复制的是1到3,然后是2到4,当复制到第三个的时候,原本的3已经被换成了开始的1,那么就变成把1复制到5,显然不是我们想要的。那该如何处理?
我们可以从src的最后一个整形数据3开始拷贝,这样3和5,2和4,1和3,就不会出现上面的情况了。
也就是说如果dest的地址大于src,就从src的最后一个元素开始拷贝,反之从第一个元素开始拷贝。
#include<assert.h>
void my_memmove(void* dest,void* src,size_t num)
{
assert(dest && src);
if(dest>src)
{
while(num--)
{
*((char*)dest+num) = *((char*)src+num);
}
}
else
{
int i = 0;
for(i = 0;i<num;i++)
{
*((char*)dest+i) = *((char*)src+i);
}
}
}
我们用一个数组来试一下
结果是正确的。
2.3memcmp的实现
#include<assert.h>
int my_memcmp(void* dest, void* src, size_t num)
{
assert(dest && src);
int i = 0;
while (i<num)
{
if (*((char*)dest + i) > *((char*)src + i))
{
return 1;
}
else if(*((char*)dest + i) < *((char*)src + i))
{
return -1;
}
i++;
}
return 0;
}
这个代码比较思路比较简单,如果某个字节大就返回1,否则返回-1,循环结束仍无返回就返回0,表示相等。这里的实现方式可能和vs底下的实现方式可能有所不同,不相等的情况vs可能就直接返回数据的差值。
拿一个代码来举一个例子
int main()
{
int arr1[] = { 1,3,4,5 };
int arr2[] = { 1,0x00001103 };
printf("%d", my_memcmp(arr2, arr1, 8));
return 0;
}
执行的结果