目录
一.memcpy函数
void* memcpy(void* destination, const void* source,size_t num);
//传参后使用void*接收,说明这个函数不论是什么类型都会执行。
-
size_t表示无符号数字
-
函数memcpy从source的位置开始向后复制num个
字节
的数据到destination的内存位置。 -
这个函数在遇到’\0’的时候并不会停下来。
-
如果source和destination有任何的重叠,复制的结果都是未定义的。
例题
例1:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr, 20);
return 0;
}
例二:
int main()
{
float arr1[] = { 1.0f,2.0f,3.0f,4.0f };//数值后加f表示该数为浮点数
float arr2[5] = { 0 };
memcpy(arr2, arr1, 8);
return 0;
}
模拟实现:
//这种写法不是最好的写发,有些编译器是编不过去的
#include<assert.h>
void* my_memcpy(void* destination, const void* source,size_t num)
{
assert(destination);
assert(source);
void* ret = destination;
for (int i = 0; i < num; i++)
{
*((char*)destination)++ = *((char*)source)++;
}
return ret;
}
//这种方法可以在所以编译器中使用
void my_memcpy(void* destination, const void* source,size_t num)
{
assert(destination);
assert(source);
void* ret = destination;
while(num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
return ret;
}
- 当我们测试source和destination在一个数组并且它们拷贝的范围部分重合时,会发生错误。
例如使用下面的代码传递:
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr+2, arr, 20);
内存展示为:
而编译器定义的memcpy函数却不是这样定义的,memcpy的结果为:
这并不是说之前写的模拟函数有问题,在这里我们需要明白一点,在
C语言规定
中,那段错误的结果才是memcpy真实的结果,但我使用的是VS编译器,由于编译器的不同,它将memcpy改的更为强大了而已,其它编译器并非如此。在C语言规定中,当拷贝的两组数地址部分重合时,应该使用memmove函数,memmove函数的功能是
强于
memcpy的,接下来讨论的编译过程也是属于memmove的但在VS编译器当中两者差异不大,这里以
C语言规定
为准,但大家在平时练习的时候需要注意这一点。
二.memmove函数
void* memmove(void* destination,const void* source,size_num);
-
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
-
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
地址重叠
当拷贝的两组数出现重叠时,让我们看看memmove函数是如何处理的。
这个时候我们就要分类讨论了:
-
des == destination
-
sou == source
-
num == 20;
-
此时des和sou分别是destination和source所表示的地址
des>sou
- 如图,当des的地址大于sou时我们就需要从sou最后一位数开始拷贝,这样才能防止被覆盖。
des<sou
- 这个时候我们就要从前依次往后拷贝,才能顺利完成拷贝。
模拟实现
void* my_memmove(void* destination, const void* source, size_t num)
{
void* tem = destination;
if (destination < source)
{
for (int i = 0; i < num; i++)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
else
{
destination = (char*)destination + num - 1;
source = (char*)source + num - 1;
for (int i = 0; i < num; i++)
{
*(char*)destination = *(char*)source;
destination = (char*)destination - 1;
source = (char*)source - 1;
}
}
return tem;
}
三.memset函数
- 此函数目前我见到使用最多的场景是用来
初始化一块连续的空间(可以看作数组)
。在第一次完成这篇博客时,我没想着把这个函数加入其中,我感觉它不是很重要,在今天做题的时候,勾起我对这个memset复杂的回忆。
之前做题时,创建数组后初始化都是进行循环一个个初始化,一次看题解不经意的一瞥,注意到这个函数,用了以后感觉是真香。但毕竟做题少而且平时练习的时候都是直接给数组初始化为0,不像做题的时要自己malloc一个新的空间。
void* memset(void* ptr,int value,size_t num);
- sets the first num bytes of the block of memory pointed by ptr the specified value(interpreted as an unsigned char).
- memset函数为内存填充函数
- num表示填充多少个字节
- value表示填充的值
例题:
int main()
{
int arr1[] = { 1,2,3,4,5 };
memset(arr1, 9, 8);
return 0;
}
arr1数组最开始的内存:
memset后:
- 将前8个字节全都填充为9
使用场景:
int main()
{
int* arr = (int*)malloc(sizeof(int) * 5);//开创了5个int大小的空间
memset(arr, 0, 20);
return 0;
}
memset前:
memset后:
总结:
- 按找C语言的标准来说,memmove的适用范围要比memcpy的更广
- 这两个函数是以字节为单位,按字节改变的。
- memset常用来初始化空间