目录
[一、memcpy 使用和模拟实现](#一、memcpy 使用和模拟实现)
[(1)memcpy 的使用](#(1)memcpy 的使用)
[(2)memcpy 的模拟实现](#(2)memcpy 的模拟实现)
[二、memmove 使用和模拟实现](#二、memmove 使用和模拟实现)
[(1)memmove 的使用](#(1)memmove 的使用)
[(2)memmove 的模拟实现](#(2)memmove 的模拟实现)
[三、memset 函数的使用](#三、memset 函数的使用)
[四、memcmp 函数的使用](#四、memcmp 函数的使用)
简单介绍
mem的全拼是memory,在计算机里专门翻译叫:**内存、存储器,**我们以mem开头的函数,都是直接对内存操作的函数,我们称之为内存函数,和str开头的函数不同,str开头的函数只能处理字符串,但mem开头的函数可以用于任何类型的数据,下面让我们来看看有哪些内存函数
我们主要有下面这四大C语言内存函数:
- memcpy:内存拷贝(不支持内存重叠)
- memmove:内存拷贝(支持内存重叠)
- memset:内存初始化(清零最常用)
- memcmp:内存逐字节比较
总结:
- str 系列函数 :参数是
char*,只能处理 char 类型的字符串 ,以\0作为结束标志,不能处理 int、结构体等。- mem 系列函数 :参数是
void*,不区分数据类型,按字节操作内存,char、int、数组、结构体都能用。- memcpy、memmove、memset、memcmp 这 4 个内存函数,全部都在
<string.h>里
一、memcpy 使用和模拟实现
(1)memcpy 的使用
memcpy函数的原型与作用:
cpp
void* memcpy(void* destination, const void* source, size_t num);
- 函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。
- 需要返回destination的起始地址!!!
- 使用需要头文件#include <string.h>
cpp
#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);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
这串代码中memcpy函数是将arr1中的前二十个字节的内存,直接整块复制给了arr2,二十个字节也就是五个整型,将arr1的前五个整型复制给了arr2,arr2 剩下的 5 个元素保持初始值0,所以会打印1 2 3 4 5 0 0 0 0 0
运行截图:

对于自己复制自己这种,内存有重叠的数据,我们用memmove来处理
(2)memcpy 的模拟实现
cpp
#include <stdio.h>
#include <assert.h>
//memcpy的模拟实现
void* my_memcpy(void* des, const void* src, size_t num)
{
assert(des && src);
void* start = des;
int count = num;
while (count--)
{
*((char*)des) = *((char*)src);
((char*)des)++;
((char*)src)++;
}
return start;
}
//主函数
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
在对于memcpy函数的模拟实现中,我们首先进行了一个assert断言,保证我们的目标和要复制的空间都不是空指针,接着我们创建了一个void* start指针,用于存放任意类型的地址,将des的地址存给我们的strat指针,也用于最后的返回值,因为我们C语言规定,memcpy函数需要返回destination的起始地址,接着我们创建了一个count整型变量,用于对我们复制的字节数进行计数,复制一个字节,count就减1,在我们的while循环中,我们采取一个字节一个字节复制,所以我们将des和src都强制类型转化为char*类型,解引用时刚好访问一个字节,复制完一个字节后,des++,src也++,但只能一个字节一个字节的++,所以也需要强制类型转化为char*类型,然后接着下次复制,直到循环结束,复制完成。
运行截图:

二、memmove 使用和模拟实现
(1)memmove 的使用
memmove函数的原型与作用:
cpp
void* memmove(void* destination, const void* source, size_t num);
- 和 memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。
- mommove需返回目标空间destination的起始地址
- 使用需要头文件#include <string.h>
cpp
#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);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
return 0;
}
这串代码我们复制arr1的前20个字节的内存,也就是前五个整型元素1 2 3 4 5,从我们的arr1+2的地址处开始覆盖,也就是从arr1的第三个元素开始覆盖,结果也就是1 2 1 2 3 4 5 8 9 10
运行截图:

(2)memmove 的模拟实现
cpp
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dst, const void* src, size_t num)
{
void* start = dst;
assert(dst && src);
if (dst < src)
{
while (num--)
{
*((char*)dst) = *((char*)src);
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
while (num--)
{
*((char*)dst + num) = *((char*)src + num);
}
}
return start;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
return 0;
}
当dst的地址小于src的地址时,我们采取从src的起始地址开始以一个字节的大小从det的起始地址开始复制,也就是从前往后复制,代码和memcpy的复制方式一样,不过多赘述
当dst的地址大于src的地址时,我们采取从src的最后一个字节的地址开始以一个字节的大小从dst的最后一个字节地址开始复制,也就是从后往前复制,(char*)dst + num和(char*)src + num就能找到dst和src的最后一个字节的地址,随着num--,它们的地址刚好也一个字节一个字节往前挪,从后到前逐个字节进行复制
因为mommove需返回目标空间destination的起始地址,所以我们创建了void*类型的指针变量start
运行截图:

三、memset 函数的使用
memset函数的原型与作用:
cpp
void* memset(void* ptr, int value, size_t num);
- 将 ptr指向的内存块的前 num 个字节,逐个字节设置为指定的value值
- 使用需要头文件#include <string.h>
cpp
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "hello world";
memset(str, 'x', 5);
printf("%s", str);
printf("\n");
return 0;
}
将str的前五个字节的值,设置成字符'x',因为这是char类型的数组,所以我们修改的就是前五个字符,结果也就是xxxxx world
运行截图:

那如果是改变整型数组的值,是否还能正确修改呢?我们来看看:
cpp
#include <stdio.h>
#include <string.h>
int main()
{
int arr[5] = { 0 };
memset(arr,1,20);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
我们知道一个int类型占4个字节,这里我们数组大小为5,也就是有5个int类型的元素,总共大小为20个字节,所以我们在memset中选择将前20个字节都修改成1,希望能将数组中的元素都变成1,但真实情况真的是这样子的吗?
我们来看看运行截图:

可以看到,并没有出现我们想要的结果,为什么会这些这种结果,我们来调试内存看看:

上面这个图就是我们数组所开辟的二十个字节的内存空间,可以看到,当我们将前20个字节直接都设置为1时,在内存中存储的每一个字节都改为了1,这样我们随便拿出一个arr[i],里面存的都是0x01010101,换算成十进制就是16843009,所以才会出现上面的那种效果
四、memcmp 函数的使用
memcmp函数的原型与作用:
cpp
int memcmp(const void* ptr1, const void* ptr2, size_t num);
- 比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
- 以字节为单位进行比较,不关注数据类型
- 使用需要头文件#include <string.h>
| 返回值 | 含义 |
|---|---|
| 0 | 前 num 个字节 完全相同 |
| > 0 | ptr1 大于 ptr2 |
| < 0 | ptr1 小于 ptr2 |
cpp
#include <string.h>
#include <stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,7 };
int arr2[] = { 1,2,3,4,5 };
int n = memcmp(arr1, arr2, 17);
printf("%d\n", n);
return 0;
}
我们比较arr1和arr2两个数组,如上述代码所示,当我们比较前十七个字节时,我们的返回值会是什么?我们来看看运行截图:

为什么会是1呢,我们来调试一下看看:
这是arr1中在内存中存储的数据
这是arr2中在内存中存储的数据
可以看到,前十六个字节都是相等的,当我们比较到第十八个字节时,arr1里存的是0x07,arr2里存的是0x05,所以arr1>arr2,它们的差值是2,然后因为VS 里的 memcmp 只会返回:-1、0、1,不会返回真实字节差值,所以我们会返回一个1,打印出n的结果就是1
感谢大家的观看,新人求互三,关注我必回关!下章见!