memcpy
前面介绍了专门拷贝字符串的函数strcpy
,但是strcpy
只能拷贝字符串
如果想拷贝其他类型的内存空间,就需要用到memcpy
函数
void * memcpy ( void * destination, const void * source, size_t num );
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;
并且值得注意的是,在挪动指针的时候,平常我们都习惯使用后置++
,但是因为这里的指针为void*
类型,不能直接后置++
可以前置++如:
++(char*)des;
++(char*)src;
如果想使用后置++,也可以这样:
((char*)des)++;
((char*)src)++;
或者也可以这样:
des = (char*)des + 1;
src = (char*)src + 1;
所以根据之前的模拟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 };
如果想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 );
- 和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 );
- 比较从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 );
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);