memcpy函数
定义:memcpy指的是c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,函数返回dest地址
- 为什么要有memcpy?
我们了解同样拥有拷贝功能的函数strcpy可以实现字符串的拷贝,但是它无法实现对整型或者其他类型元素的拷贝,并且就字符串而言,它的功能有一定的局限性。
使用strcpy函数将元素皆为0的字符数组拷贝
char* my_strcpy(char*dest,const char*soc)
{
//先将各个元素进行赋值,直到前者被赋予\0.那么个根据while语句性质()内如果为o即停止
char*a = dest;//防止返回值被破坏
if (dest == soc)
{
return dest;
}
assert((soc!= NULL) || (dest != NULL));//使用断言宏来进行异常提示
while ((*dest++ = *soc++)!='\0');//此处为赋值而不是判断大小
return a;
}
int main()
{
char arr[] = "asdasdasd";
char arr2[10] = { 0 };
printf("%s",my_strcpy(arr, arr2));
system("pause");
return 0;
}
可以看到程序结束以后只有arr[0]被改变为0.
由此可知,如果想拷贝一个字符串中含有\0
的字符串至目标字符串,那么copy会在第一次遇到\0
时就终止无法实现。
所以创建了memcpy函数通过给定字节数来更改内存的方法实现拷贝,从而实现了多类型的拷贝。
- memcpy实现
由于memcpy实现了无差别拷贝,但是在memcpy函数内部必然会用到指针的加法,在未指定类型的情况下指针无法进行加法运算,因为不同类型的指针+1跳过的字节数是不同的,所以 在memcpy函数的实现中,使用void*来指定形参和函数的返回值类型,在函数内部通过(char *) 来进行指定字节数的加法。
void* my_memcpy(void* dest, const void* sor, int count)
{
assert(dest != NULL);
assert(sor != NULL);
void *ret=dest;
while (count--)//开始进行逐字节copy
{
*(char*)dest=*(char*)sor;//因为指针是void类型,所以在这里逐字节引用要强制类型转换成char*
((char*)dest)++;//因为++操作符优先级高于(类型转换)操作符。这里建议使用前置++
((char*)sor)++;
}
return (ret);
}
int main()
{
int arr[] = { 1,2,3,4,4,5 };
int arr2 [10] = { 0 };
my_memcpy(arr2, arr, sizeof(arr));
for (int i = 0; i < sizeof(arr2)/sizeof(*arr2); i++)
{
printf("%d", arr2[i]);
}
system("pause");
return 0;
}
memmove函数
memmove同样用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同
- 那种情况memcopy无法实现?
int main()
{
int arr[] = { 1,2,3,4,4,5 };
memcpy(arr+1, arr, 8);
for (int i = 0; i < sizeof(arr)/sizeof(*arr); i++)
{
printf("%d", arr[i]);
}
system("pause");
return 0;
}
这段程序的意思是以整型数组arr的首元素为起点,向后拷贝8个字节,也就是将1
,2
,这两个元素拷贝至第二个和第三个元素。
那么他的结果应该是1,1,2,4,4,5
。那么程序运行结果是?
如何理解?
由可知,如果正常从左向右交换,因为地址是重叠的,所以当arr所在行1
赋值给arr+1行2
了以后,下次赋值因为地址相同但值已经改变,所以是将1
赋值给了3
,导致结果出错。如何解决?
如果改变赋值顺序,从右向左赋值,第一步先从2
向3
赋值,那么就可以完成赋值。
可以看出当dest(arr+1)>sor(arr)时
我们需要采用从右向左的赋值方法来进行cpoy。
当dest (arr)
函数实现
void* my_memmove(void* dest, const void*sor, int count)
{
assert(dest!=NULL);
assert(sor!=NULL);
void *ret=dest;
if (dest < sor)//当存在重叠的地址的字符串dest位于sor左边时
{
while (count--)
{
*(char*)dest = *(char*)sor;//从左向右赋值
++(char*)dest;
++(char*)sor;
}
}
else
{
while (count--)//通过count--来实现从右向左赋值
{
*((char*)dest + count) = *((char*)sor + count);//如果没有count--,这里的count需要-1才能到最右边的元素
}
}
return (ret);
}int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr, arr + 2, 16);
for (int i = 0; i < sizeof(arr) / sizeof(*arr); i++)
{
printf("%d", arr[i]);
}
system("pause");
return 0;
}
总结:在函数的指针操作由很多细节需要注意,void*不能丢,在解引用时需要进行char*的强制类型转换实现指针操作,最后主要注意的时函数的实现中需要尽可能的功能全面和保证程序的健壮性