【C进阶】内存函数
创始人
2024-05-12 06:03:58
0

家人们欢迎来到小姜的世界,<<点此>>传送门 这里有详细的关于C/C++/Linux等的解析课程,家人们赶紧冲鸭!!!
客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

内存函数

  • 前言
  • 一、memcpy
    • (一)介绍
    • (二)应用
    • (三)模拟实现
    • (四)进阶:拷贝自己
  • 二、memmove
    • (一)介绍
    • (三)模拟实现
  • 三、memcmp
    • (一)介绍
    • (二)模拟实现
  • 四、memset
    • (一)介绍
    • (二)模拟实现
  • 总结


前言

在之前讲解的关于字符串的拷贝我们已经知道了strcpy,strcmp,strstr等的处理字符串的库函数,但是现在有一个情况了,倘若是数字数组呢?数字字符进行拷贝,移动和比较是不是没有办法,对,这种方法叫做内存函数,所以今天我们要介绍的是memcpy,memmove,memcmp,memset这四个内存函数,对数组是很友好的!


一、memcpy

(一)介绍

在这里插入图片描述
根据图片我们能知道我们是根据字节的大小去决定拷贝过去的数,拷贝的方法是从src拷贝到dest中去。
我们先简单使用一下,如下代码:

#include
#includeint main() {int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 20);return 0;
}

在这里插入图片描述

(二)应用

那当然了,如果你想拷贝从3开始往后拷贝4个数(20个字节)的话那我们就直接变值即可,如下代码:

#include
#includeint main() {int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1 + 2, 20);return 0;
}

在这里插入图片描述
那我们拷贝17个字节呢?能不能拷贝过去呢?答案是可以的,可是发现下面的数据怎么拷贝了3,4,5,6,7过去呢?因为是数据在计算机中存放的是01 00 00 00小端存放,一直到7的位置存放的是07 00 00 00,所以取到了07,所以7能输出。
在这里插入图片描述

(三)模拟实现

那解释后上完整代码(后面赋代码):
在这里插入图片描述
根据画图我们知道,当我们想拷贝过去的时候,我们并不知道这个指针是什么类型的,是整型?是字符?那不确定我们看着个传参过去的是字节大小,所以我们就想到了强制类型转换成为char*指针一个字节一个字节往后找即可。

#include
#include
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {assert(dest && src);//保存首元素地址void* ret = dest;while (num--) {//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可//一次强转是临时的*(char*)dest = *(char*)src;//强制类型转换dest = (char*)dest + 1;src = (char*)src + 1;/*++(char*)dest;++(char*)src;*/}return ret;
}int main() {int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };//不确定arr2和arr1里面是什么类型的指针my_memcpy(arr2, arr1 + 2, 17);return 0;
}

(四)进阶:拷贝自己

那我们就有个比较大胆的想法,我们能不能实现一下在自身字符串处移动,我们按照原本的套路试一下:

#include
#include
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {assert(dest && src);//保存首元素地址void* ret = dest;while (num--) {//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可//一次强转是临时的*(char*)dest = *(char*)src;//强制类型转换dest = (char*)dest + 1;src = (char*)src + 1;/*++(char*)dest;++(char*)src;*/}return ret;
}void test1() {int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };//不确定arr2和arr1里面是什么类型的指针my_memcpy(arr2, arr1 + 2, 17);
}void test2() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr + 2, arr, 20);}int main() {//test1();test2();return 0;
}

我们发现test2()函数看的样子很正确,可是真的这样吗?我们进入调试看一看吧!
在这里插入图片描述
这输出结果怎么是1 2 1 2 1 2 1 8 9 10,跟我们的预期结果1 2 1 2 3 4 5 8 9 10怎么差别那么大呢?我们画图解释一下:
在这里插入图片描述
发现当我们想要找替换3的时候,3已经被1覆盖了,只能复制1了,所以是错误的,那这种情况我们需要分情况讨论了:
以下情况是重叠的情况:
情况一:dest(被拷贝的数据)指针在src(源头数据)指针的左边时,需要从左往右拷贝,也就是从前往后拷贝。在这里插入图片描述

情况二:dest(被拷贝的数据)指针在src(源头数据)指针的右边时,需要从右往左拷贝,也就是从后往前拷贝。在这里插入图片描述

不重叠情况:从前往后拷贝和从后往前拷贝都可以。

那这么复杂的情况,早期的计算机编程师肯定想到过,所以就另外制作了一个库函数memmove来解决这种情况,那我们接下来介绍一下memmove库函数吧!

二、memmove

(一)介绍

首先的首先我们,先看一下这个函数在MSDN的介绍:
在这里插入图片描述
有了上面的概念和我们需要进行移动的想法,我们直接用memmove实现一下吧:

在这里插入图片描述
在这里插入图片描述
上面两串代码就是memmove的魅力,不管是从前往后还是从后往前,memmove都可以进行移动。

(三)模拟实现

既然有了上面的思路,那我们模拟实现就很轻松了,只要是dest在src左边的时候,那就是统一从前往后拷贝;dest在src右边的时候,那就是统一从后往前拷贝,注意,这里的dest和src是头指针:

#include
#include
void* my_memmove(void* dest, const void* src, size_t num) {assert(dest && src);//保存首元素地址void* ret = dest;if (dest < src) {//从前向后拷贝while (num--) {//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可//一次强转是临时的*(char*)dest = *(char*)src;//强制类型转换dest = (char*)dest + 1;src = (char*)src + 1;/*++(char*)dest;++(char*)src;*/}}else {//从后向前拷贝while (num--) {//第一次往后跳了19个字节*((char*)dest + num) = *((char*)src + num);}}return ret;
}
void test3() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr, arr + 2, 20);}int main() {//test1();//test2();test3();return 0;
}

三、memcmp

(一)介绍

我们先看一下MSDN的介绍:
在这里插入图片描述
即;比较从ptr1和ptr2指针开始的num个字节。
那我们根据介绍进行制作一下这个吧!

在这里插入图片描述
如图所示,当我们在进行比较的时候,是根据字节大小比较的,所以这就取决于编译器是大端还是小端,根据字节在内存中的存放顺序进行比较的,我们知道,在VS环境下,是小端存储,也就是说5是0x05 00 00 00,03是0x03 00 00 00,所以取第一个字节,较大的是05,所以当字节数为不是4的倍数的时候,我们也可以比较:
在这里插入图片描述

(二)模拟实现

#include
#include
int my_memcmp(const void* buf1, const void* buf2, size_t count) 
{assert(buf1 && buf2);//往后找字节进行比较while (count--) {if (*(char*)buf1 > *(char*)buf2) {return 1;}else if (*(char*)buf1 < *(char*)buf2) {return -1;}buf1 = (char*)buf1 + 1;//往后移动buf2 = (char*)buf2 + 1;}return 0;
}int main() {int arr1[] = { 1,2,3 };int arr2[] = { 1,2,5 };int ret = my_memcmp(arr1, arr2, 9);printf("%d\n", ret);return 0;
}

四、memset

(一)介绍

名为内存设置函数,是以字节为单位来设置内存中的数据的。
在这里插入图片描述
如下所示,根据MSDN介绍我们进行书写:
在这里插入图片描述

(二)模拟实现

#include
#includevoid* my_memset(void* dest, int c, size_t count)
{assert(dest);//头指针给存起来void* start = dest;//循环相赋值while (count--) {*(char*)dest = (char)c;dest = (char*)dest + 1;}return start;
}int main()
{char arr[] = "hello world!";char* ret = my_memset(arr, 'x', 5);printf("%s\n", arr);return 0;
}

总结

当我们使用这些函数的时候,我们需要搞清楚这些函数的传参以及我们需要注意的事情,尤其是我们需要熟悉并掌握模拟实现这些内存函数的使用,这个是很关键的,对于自己是有很大的提升的!!!


客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...