memcpy
前面介绍了专门拷贝字符串的函数strcpy,但是strcpy只能拷贝字符串
如果想拷贝其他类型的内存空间,就需要用到memcpy函数
void * memcpy ( void * destination, const void * source, size_t num );
1
memcpy在string.h文件中,使用时应包含头文件。
这里的num是字节数。
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
#include <stdio.h> #include<string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] = { 0 }; memcpy(arr2, arr1, 20);//将arr1中的前20个字节拷贝到arr2空间中 }
memcpy(arr2, arr1, 20);,是将arr1中的前20个字节拷贝到arr2空间中,20个字节也就是前5个int类型的元素
运行程序,进入调试监控窗口可以看到,已经成功将arr1中前20个字节的内容拷贝到了arr2中
如果想拷贝3、4、5、6、7,可以变源头,memcpy(arr2,arr1+2,20)即可
模拟实现memcpy
因为我们不知道要拷贝的类型是什么,所以就需要用void*接收目标空间地址和源空间地址,所以函数定义为:void* my_memcpy(void* des, void* src, size_t num)
接下来,因为不知道拷贝的是什么类型,所以在拷贝的时候,需要将void*类型的指针强制转换成char*类型,这样可以一个字节一个字节地进行拷贝
*(char*)des = *(char*)src;
1
并且值得注意的是,在挪动指针的时候,平常我们都习惯使用后置++,但是因为这里的指针为void*类型,不能直接后置++
可以前置++如:
++(char*)des; ++(char*)src;
1
2
如果想使用后置++,也可以这样:
((char*)des)++; ((char*)src)++;
1
2
或者也可以这样:
des = (char*)des + 1; src = (char*)src + 1;
1
2
所以根据之前的模拟strcpy以及上述的几点,可以写出模拟memcpy函数:
//memcpy #include <stdio.h> #include <assert.h> #include<string.h> void* my_memcpy(void* des, void* src, size_t num) { assert(des && src); void* ret = des; while (num > 0) { *(char*)des = *(char*)src; des = (char*)des + 1; src = (char*)src + 1; /*((char*)des)++; ((char*)src)++;*/ num--; } return ret; }
memmove
前面讲了memcpy函数,但是如果在同一块内存空间上使用mencopy函数也许会有问题:
如果有一个数组
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
1
如果想4、5、6、7拷贝到6、7、8、9的位置上时就会出错误
在将 4 和 5 拷贝到原先 6 和 7 的位置上后,接着拷贝,本来是应该将 6 拷贝到 8 的位置上,结果本应是6的位置却在之前的拷贝被 4 占了,就出了错误
所以为了解决这问题,就有了memmove函数,它即包含了之前memcpy的功能,也可以叠加拷贝
void * memmove ( void * destination, const void * source, size_t num );
1
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include <stdio.h> #include<string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1+2, arr1, 20); }
模拟实现memmove
在模拟实现前,我们先西靠一下如果解决重叠的拷贝问题
当des在src前面时,我们发现,可以从前往后进行拷贝,不会出现问题
如图:
把 4 拷贝到 1 的位置上
把 5 拷贝到 2 的位置上
把 6 拷贝到 3 的位置上
把 7 拷贝到 4 的位置上
不会出现被覆盖数据的问题
当des在src后面时,如果还从前往后拷贝就会出现被覆盖的问题,所以需要从后往前拷贝
把 7 拷贝到 9 的位置上
把 6 拷贝到 8 的位置上
把 5 拷贝到 原先 7 的位置上
把 4 拷贝到 原先 6 的位置上
只有这么倒着拷贝,就不会有问题
模拟实现memmove:
void* my_memmove(void* des, void* src, size_t num) { assert(des && src); void* ret = des; if (des < src) { while (num > 0) { *(char*)des = *(char*)src; des = (char*)des + 1; src = (char*)src + 1; num--; } } else { char* src_pos = (char*)src + num-1; char* des_pos = (char*)des + num-1; while (num>0) { *des_pos = *src_pos; src_pos = src_pos-1; des_pos = des_pos-1; num--; } return ret; }
这里值得注意的一点时,当倒着拷贝时,首先要把src空间中最后一个字节内容拷贝到des空间的最后一个字节中
num是函数传进来一个表示字节的参数,src空间中最后一个字节内容不是:(char*)src + num,而是(char*)src + num-1
原因如下图:
可以对上面的代码进行简化:
void* my_memmove(void* des, void* src, size_t num) { assert(des && src); void* ret = des; if (des < src) { while (num > 0) { *(char*)des = *(char*)src; des = (char*)des + 1; src = (char*)src + 1; num--; } } else { while (num--) { *((char*)des + num) = *((char*)src + num); } } return ret; }
这里为了保证让最开始拷贝的地址为src+num-1,在while条件中,就已经num--一次了,就保证了从最后一个字节开始拷贝
memcmp int memcmp ( const void * ptr1,const void * ptr2,size_t num ); 1
比较从ptr1和ptr2指针开始的num个字节
返回值情况同strcmp函数
模拟实现memcmp
int my_memcmp(void* ptr1, void* ptr2, size_t num) { assert(ptr1 && ptr2); while (num > 0) { while (*(char*)ptr1 == *(char*)ptr2) { if (num == 1) { return 0; } ptr1 = (char*)ptr1+1; ptr2 = (char*)ptr2+1; num--; } if (*(char*)ptr1 < *(char*)ptr2) { return -1; } if (*(char*)ptr1 > *(char*)ptr2) { return 1; } } } memset void * memset ( void * ptr, int value, size_t num ); 1 memset为内存设置函数 以字节为单位来设置内存中的数据 #include <stdio.h> #include <string.h> int main() { char arr[] = "hello world"; memset(arr, 'x', 5); printf("%s", arr); }
把arr的前5个字节内容改为'x',输出结果:
有一个int类型的数组int arr[10] = {0},如果使用memset(arr, 1, 40);会有什么结果呢?
#include <stdio.h> #include <string.h> int main() { int arr[10] = {0}; memset(arr, 1, 40); }
进入监视就可以看到,数组内的数据有0变为了一个很大的数
这是因为memset函数将这个数组40个字节全部复制为1,而不是将每个整形赋值为1,二进制为00000001000000010000000100000001的数字就是16843009
这里可以看出,memset函数对于字节数为1的字符类型很适用,对于2、4、8字节的类型不太适用
这里有一种对于2、4、8字节适用的情况,就是把他们的各个字节都变为0,也就是把变量赋值为0
int arr[10] = { 0 }; memset(arr, 0, 40);