一.对库函数memcpy的了解
通过在MSDN或者cplusplus网站上检索memcpy
通过对memcpy的检索,可以初步了解到memcpy以下信息:
1.有三个参数,目标指针,源头指针均为泛型指针 void* () (具有通用性,且不能直接被解引用)
2.该函数不检查源中是否有任何终止空字符 , 它始终精确地复制数字字节
3.为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 个字节
4.目标数据和源数据不能重叠
二.模拟实现库函数memcpy
思路分析:
1.通过对库函数memcpy的了解,它的前面两个参数都是void类型的泛型指针,无法直接解引用,并且用户使用时候可能传入的是int 、char、float等类型中的一种,这对传入后复制多少个字节的内容带来了一定难度,因此这个void的参数应该转换为什么类型是我们应该首要考虑的
假设如下图,将参数强转为int类型进行操作,那么arr+1将跳过四个字节,如果传入的size_t num=6,那么将对第二个字节的内容读取前面两个字节的内容,进行解引用以后会产生影响,因此,在这应当选择操作力度最小的char类型进行操作,一次跳过一个字节,就能很好的规避上面的问题
2.循环num次,将源头数据的内容覆盖到目标数组中
代码如下:
#include <stdio.h> #include <assert.h> void* my_memcpy(void* str1, const void* str2, size_t num) { assert(str1 && str2); void* p = str1;//返回目标地址,记录目标地址起始位置 while (num--) { *(char*)str1 = *(char*)str2; str1 =(char*)str1+1; str2 =(char*)str2+1; } return p; } int main() { char arr1[] = "abcdef"; char arr2[20] = "hh"; my_memcpy(arr2, arr1, 3); printf("%s\n", arr2); return 0; }
运行结果如下:
三.对库函数memcpy的具体说明
1.如果需要复制int类型的数据该如何
若为其他类型,在传参时,传入的字节数尤为重要,只需要将你想要的复制的元素个数乘上类型的大小即可
#include <stdio.h> #include <assert.h> void* my_memcpy(void* str1, const void* str2, size_t num) { assert(str1 && str2); void* p = str1;//返回目标地址,记录目标地址起始位置 while (num--) { *(char*)str1 = *(char*)str2; str1 =(char*)str1+1; str2 =(char*)str2+1; } return p; } int main() { int arr1[] = {1,2,3,4}; int arr2[20] = {0}; int sz=sizeof(arr1)/sizeof(arr1[0]); my_memcpy(arr2, arr1, 3*sizeof(int)); for(i=0;i<sz;i++) { printf("%d ",arr2[i]); } return 0; }
运行结果如下:
2.它始终精确地复制数字字节
对于开头提到的该函数不检查源中是否有任何终止空字符 ,它始终精确地复制数字字节。这句话表明无论源头数据是否具有\0,它会连同\0一同复制到目标数据中。
代码如下:
int main() { char arr1[] = "a\0bcdef"; char arr2[20] = "hh"; my_memcpy(arr2, arr1, 3); printf("%s\n", arr2); return 0; }
运行结果如下:
可以看到,原本应当打印a\0b,由于打印的时字符串,因此只打印了a,通过观察内存可以验证该结果
调试观察内存可以看到:
因此,库函数memcpy会将num个字节的内容复制到目标空间中,终止符也可能会被复制。
3.覆盖目标空间的内容
通过(2)上面代码的运行结果可以看到,arr2中原本有“hh”这个字符串,但是最后结果内存中却只有a\0b无论arr2这个源头空间中原本是否有数据,memcpy都会将其覆盖。
4.目标数据和源数据不能重叠(重点理解)
当出现下面这种情况时,数据会出现丢失
例如:
int main() { char arr1[] = "abcdef"; char arr2[20] = "hh"; my_memcpy(arr1+2, arr1, 4); // str1 str2 printf("%s\n", arr1); return 0; }
当a覆盖到c的位置时,c已经被覆盖成了a,st2指向原本c的位置时,拷贝给st1的内容就是a了,不是原本的c造成了数据的丢失:
运行结果如下:
因此,目标数据与源头数据的内存不能重叠,否则就会发生数据拷贝内容被覆盖,数据出现偏差。
5.防止越界
由于库函数memcpy时将源头的num个字节的内容覆盖到目标内存中,因此,目标内存有可能出现越界情况。
当出现下面情况时,会发生越界崩溃
int main() { char arr1[] = "abcdef"; char arr2[20] = "hh"; my_memcpy(arr1+4, arr1, 4); // str1 str2 printf("%s\n", arr1); return 0; }
st1向后访问num个字节以后比sizeof(arr1)大的情况下,会发生越界,应当使用时防止该情况发生。
运行结果如下: