内存函数

  • 1.前言
  • 2.内存函数讲解及各自模拟实现
    • 2.1.memcpy
      • 2.1.1.讲解
      • 2.1.2.模拟实现
    • 2.2.memmove
      • 2.2.1.讲解
      • 2.2.2.模拟实现
    • 2.3.memset
      • 2.3.1.讲解
    • 2.4.memcmp
      • 2.4.1.讲解
  • 3.完结撒花

1.前言

2.内存函数讲解及各自模拟实现

2.1.memcpy

2.1.1.讲解

2.1.2.模拟实现

2.2.memmove

2.2.1.讲解

2.2.2.模拟实现

2.3.memset

2.3.1.讲解

2.4.memcmp

2.4.1.讲解

3.完结撒花

创作不易,点赞关注,感谢支持qaq

1.前言

所谓内存函数,与指针地址一样,都是基于内存上面完成的函数,与其他一些基于数值运算不同,其相当于在根本上完成了运算,之前我在《函数栈帧的创建与销毁》这篇博客中讲过,每一次函数调用都是在内存上开辟了一块新的空间,所以我们所敲的代码,运算最根本是在内存中完成,所以我们深入学习就必须认清内存存储的原理,在此之前建议大家把指针,地址的知识弄明白,再进行内存函数的学习。这节所讲的内存函数都是基于内存进行操作的,一些比较抽象的地方我会画图帮助大家理解。

2.内存函数讲解及各自模拟实现

2.1.memcpy

2.1.1.讲解

memcpy与之前所讲的字符串拷贝函数strcpy,对于字符串的使用作用一样,但是原理不同,并且memcpy不仅可以拷贝字符串,对于结构体,整形数组等也能进行拷贝,所以这就不难理解memcpy是基于内存进行拷贝的了。下面我们可以在MSDN上面查找该内存函数,先观察其形参是如何传递的,见下图:
可以看到memcpy其所包含的头文件可以是string.h或者是memory.h,其形参传递形式为两个泛型指针,一个是源头src即拷贝的对象,另一个是目的地dest即要进行拷贝的目的地,count是表示要拷贝的字节数,注意这里进行内存拷贝所拷贝的长度单位为字节。
下面我们来拷贝一个整形数组给大家进行讲解,见下图:
我们是将arr1中的前5个数据进行拷贝,拷贝到arr2中,需要注意的就是后面拷贝的字节数据是如何进行传参的。
对于memcpy基于内存进行操作我们如何来观察,可以对代码进行调试,然后点击[调试] -> [窗口] -> [内存],打开两个内存监视窗口,这里所展示的就是内存中的存储,下面是拷贝完成后的内存情况:
arr1:
arr2:
可以看到内存存储显示也是发生了变化。
最后在MSDN上的查找中可以看到,memcpy的返回类型是void*,其实在完成了拷贝之后,memcpy函数会返回拷贝目的地的首地址即arr2的首地址,不过类型为void*类型,如下图我们以拷贝字符串为例:

虽然编译器可以运行,但是会报警告
我们需要对其进行强制类型转换,如下图所示:

代码如下:

int main(){char arr1[10] = "12345aaaaa";char arr2[10] = { 0 };char* p1 = (char*)memcpy(arr2, arr1, 6 * sizeof(char));printf("%s", p1);return 0;}

这里我们还需要注意memcpy在拷贝字符串时,遇见\0也不会停止拷贝,其会将所传的字节范围的数据全都拷贝下来,见下图:
我们可以看到memcpy进行拷贝后,arr2数组中连同arr1中的\0也进行了拷贝。

2.1.2.模拟实现

我们知道memcpy的原理,下面我们仿照memcpy函数自己上手进行一下模拟实现与其功能一样的函数,实现代码如下:

void* my_memcpy(void* dest, const void* sor, size_t count){assert(dest && sor);void* p1 = dest;while (count--){*(char*)dest = *(char*)sor;dest = (char*)dest + 1;sor = (char*)sor + 1;}return p1;}int main(){int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr1+2, arr1, 6 * sizeof(int));for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;}

上面代码就对memcpy进行了实现,可以看到我们所设计的my_memcpy与
在MSDN上面搜索的memcpy的结构是一致的,这保证了我们所实现的功能与memcpy一样。
这里我们需要知道memcpy对重叠部分的拷贝是不做处理的,什么是重叠的意思?我们看下图:
可以看到我们要做的操作是将arr1的数组前26字节的数据拷贝到arr1+2中,可能有人不知道arr1+2是什么意思,这里的arr1表示的是数组首元素地址,加2则指向数组arr1的第三个元素的地址,那么按照我们之前所讲memcpy进行拷贝,所打印出来的结果应为1,2,1,2,3,4,5,6,9,10,才对,但其打印结果与所推理并不符合,其实我们对这打印出来的结果仔细观察便可以发现,从arr1[4]和arr1[5]开始,其拷贝的是arr1[2]和arr1[3]上的数据,而arr1[2]和arr1[3]上的数据在此之前已经进行了拷贝,所以数据便会重叠,进而形成所打印的1,2,1,2,1,2,1,2,9,10.
所以memcpy不会处理重叠的情况,但在一些编译器上面比如VS2022中使用memcpy拷贝重叠数据会被处理,进而打印出想要的结果,这是因为在此编译器中memcpy函数是被进行了优化处理过的,而在标准的C语言规定中memcpy确实对于拷贝重叠数据的情况是不做处理的
那么问题来了,如果我们在拷贝时对于重叠状况需要处理的话,我们应该怎么办,下面就让我们进入memmove函数的讲解。

2.2.memmove

2.2.1.讲解

大家可以理解为,memcpy是对于重叠情况不做处理的内存拷贝函数,而memmove是对重叠情况做出相应处理的内存拷贝函数。我们也在MSDN
上面查看一下该函数的构造:
可以看到该函数的构造与memcpy完全一样,下面我们看下图:
可以看到对于重叠的情况已经做出处理

2.2.2.模拟实现

对于memmove的模拟实现我们也相应的要对重叠部分做出处理,我们先把模拟实现代码奉上:

void* my_memmove(void* dest, const void* sor, size_t count)//实现这个函数需要分情况考虑是从前向后还是从后向前进行拷贝{assert(dest && sor);void* p1 = dest;//前 -> 后if (sor > dest){while (count--){*(char*)dest = *(char*)sor;dest = (char*)dest + 1;sor = (char*)sor + 1;}}else//后 -> 前{while (count--){*((char*)dest + count) = *((char*)sor + count);}}return p1;}int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr + 2, arr, 6 * sizeof(int));int i = 0;for(i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;}

要想实现memmove函数的模拟,我们就需要对于重叠部分进行分析,对于拷贝起源地址sor和拷贝目的地址dest的重叠部分一共有两种可能:
下面我进行画图来方便大家理解,我们假设在一个整型数组内要拷贝20个字节。
1.sor > dest,即起源地址sor的地址高于目的地址dest:
2.sor < dest:
对于第一中重叠我们就可以进行从前向后的拷贝,结果也不会出现重叠的状况,而对于第二种我们如果还是进行从前向后的拷贝,那么打印的结果就会出现重叠的情况,因为我们在对4,5进行拷贝后,此处地址中所存储的是2,3,而我们继续拷贝在sor中已经拷贝到原先4处的地址,而现在4处存放的是2,而5处存放的是3,所以就会出现重叠的情况。所以我们在sor < dest的情况中要进行从后向前的拷贝,即从sor中的6开始向前,与dest中的8开始向前进行拷贝,这样就不会出现重叠的状况。

2.3.memset

2.3.1.讲解

memset为内存设置函数
上图可知,memset函数是设置了三个形参,其所包含的头文件可以是memory.h或string.h,其功能是设置内存,将内存中的值以字节为单位设置成想要的内容:
上图我们就将arr数组的前5个字节在内存中设置成了x。
这里我们需要注意的是,看下面举例:
这里我们将arr数组中的40个字节设置成了1,其是以字节为单位进行设置的,所以并不是将数组arr中的值都改成了1.

2.4.memcmp

2.4.1.讲解

memcmp与函数strcmp相似,不过memcmp是基于内存进行比较的,如下图:
所以总体归类,memcmp为比较函数,当产生大于值时返回的是大于0的数字,当产生小于值时返回的是小于0的数字。
当然,其比较也是以字节为单位进行,上图我们比较16个字节,而对于arr1和arr2中前16字节是相同的,所以返回的是0.
我们在比较17个字节的时候,arr2中第17个字节为6,大于arr1的5,所以返回的是小于0的数字。

3.完结撒花
今天的知识分享到此为止,如果对你有帮助希望可以点赞收藏一下,今后我也还会分享更多在学习路上遇到的知识,希望我们可以一起学习共同进步。