【C语言】内存函数的详细教学和模拟实现

🚀write in front🚀
🔎大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
🆔本文由 gugugu 原创 CSDN首发🐒 如需转载还请通知⚠
📝个人主页:gugugu—精品博客
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏:gugugu的精品博客
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

在这里插入图片描述
vs 启动

前言

上一篇博客里讲到了字符函数和字符串函数,那么在这一篇博客中,我们将另一种常见的函数讲解一下,就是内存函数,内存函数比字符函数和字符串函数更加的广泛,毕竟是针对内存的函数。
在这里插入图片描述

一、内存函数与字符串函数的区别

C语言内存函数,是针对内存块的,不在乎内存中的数据,但是字符串函数时针对字符串的,在乎内存中的数据,只操作字符串,与\0操作符关系密切。

二、memcpy函数

memcpy函数与strcpy函数功能比较相似,都是进行拷贝操作,但是memcpy针对的对象不同。

1、memcpy函数的基本结构

void* memcpy(void * destination ,const void * source,size_t num);
函数有三个参数,分别为起始地址,目标地址和移动的字节的大小,返回值是void*

  • 那么为什么起始地址和目标地址,以及返回值都是void类型呢?

在这里插入图片描述

因为memcpy函数针对的对象是内存空间,而内存空间中储存的数据类型不清楚,有多种可能性,所以直接使用void*类型的指针,在使用时,进行强制类型转换。

另外,在这里补充一点
在上一篇文章里面,很多字符串函数的返回值都是一个指针,这是为什么呢?

在这里插入图片描述

其实,这是为了能够通过返回值去更方便的进行链式访问

2、memcpy函数的模拟实现

在模拟实现memcpy这些内存函数的时候,主要是要注意对void*的强转,这比较巧妙。

这里提供两种方法。大同小异
方法一

#include <stdio.h>
#include <assert.h>void* my_memcpy(void* ch1, const void* ch2, size_t num)
{assert(ch1 && ch2);void* ret = ch1;int i = 0;for (i = 0; i < num; i++){*((char*)ch1)++ = *((char*)ch2)++;}return ret;
}int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5,6,7,8 };
void * ret1=my_memcpy(arr1, arr2, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(((int*)ret1)++));}printf("\n");char ch1[] = "ZZZZZZZZZZZ";char ch2[] = "YYYYYYYYY";void * ret2=my_memcpy(ch1, ch2, 6);printf("%s\n", (char *)ret2);return 0;
}

方法二

#include <stdio.h>
#include <assert.h>void* my_memcpy(void* ch1, const void* ch2, size_t num)
{assert(ch1 && ch2);void* ret = ch1;while (num--){*(char*)ch1 = *(char*)ch2;ch1 = (char*)ch1 + 1;ch2 = (char*)ch2 + 1;}return ret;
}int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5,6,7,8 };
void * ret1=my_memcpy(arr1, arr2, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(((int*)ret1)++));}printf("\n");char ch1[] = "ZZZZZZZZZZZ";char ch2[] = "YYYYYYYYY";void * ret2=my_memcpy(ch1, ch2, 6);printf("%s\n", (char *)ret2);return 0;
}

memcpy函数针对的对象是内存空间,所以对整形和字符都可以处理

三、memmove函数

1、memmove函数的优势

memcpy函数在使用时会存在问题,比如目标空间和起始空间发生了重叠,此时使用memcpy函数就会出现问题。
看下面的例子
在这里插入图片描述

#include <stdio.h>
#include <assert.h>void* my_memcpy(void* ch1, const void* ch2, size_t num)
{assert(ch1 && ch2);void* ret = ch1;while (num--){*(char*)ch1 = *(char*)ch2;ch1 = (char*)ch1 + 1;ch2 = (char*)ch2 + 1;}return ret;
}int main()
{int arr[10] = { 1,2,3,4,5,6,7 };my_memcpy(arr + 2, arr, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

按照设想,答案应该是 1 2 1 2 3 4 5 0 0 0
但是实际答案是
在这里插入图片描述
是不是没想到?
在这里插入图片描述

这是为什么呢?
主要是在实现的时候,读取到第三个数的时候,本来是3,但是被赋值之后就变成了1,所以第三个数也就成了1,而不是三,后面也是一样。

但是memmove函数可以解决这个问题
在这里插入图片描述

2、memmove函数的模拟实现

像上面实现memcpy一样从前面向后面拷贝出现了问题,那么如果从后面往前面拷贝,又当如何?
在这里插入图片描述

这是就会先将5放到arr[6]上,4放到arr[5]上,依次类推,可以发现,不会出现问题。

但是又有新的问题,如果是memmove(arr,arr+2,20),这又会怎么办呢?

这是从后往前就不行了,就得从前往后拷贝。

聪明的小伙伴,看到这里肯定能够想出解决方案。

  • 当目的地址比起始地址大时,从后往前拷贝
  • 当目的地址比起始地址小时,从前往后拷贝

上代码
在这里插入图片描述

#include <stdio.h>
#include <assert.h>
void* my_memmove(void* ch1, const void* ch2, size_t num)
{assert(ch1 && ch2);void* ret = ch1;if (ch1 > ch2){while (num--)//自减操作后num已经是19了{*((char*)ch1 + num) = *((char*)ch2 + num);//每次自减操作后,num都会少1,向前走了一个字节}}else{while (num--){*(char*)ch1 = *(char*)ch2;ch1 = (char*)ch1 + 1;ch2 = (char*)ch2 + 1;}}return ret;
}int main()
{int arr1[10] = { 0 };int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr2, arr2+2, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;
}

四、memset函数

这个函数比较简单,就不详细讲解了
在这里插入图片描述

1、memset函数的功能

set的意思是设置,我们在这里把它理解成赋值,就是给内存去赋值

先写段代码看看功能吧

#include <stdio.h>int main()
{char ch[20] = "hello world!";memset(ch, 'x', 10);printf("%s\n", ch);return 0;
}

在这里插入图片描述
需要注意的是

memset函数的结构比较特殊
void * memset(void* ptr,int value,size_t num);
第二个参数是int类型,为啥我的例子里面给的是char呢?

是因为char是使用ASCII码值进行操作的。

2、memset函数的模拟实现

比较简单,直接上代码

#include <stdio.h>
#include <assert.h>
void* my_memset(void* ch, int value ,size_t num)
{assert(ch);void* ret = ch;while (num--){*(char*)ch = value;ch = (char*)ch + 1;}return ret;}
int main()
{char ch[20] = "hello world!";my_memset(ch, 'x', 10);printf("%s\n", ch);return 0;
}

在这里插入图片描述
运行成功,yeah

五、memcmp函数

这个函数也比较简单,就是对内存进行比较

1、memcmp函数的基本结构

int memcmp(const void * ptr1,const void* ptr2,size_t num);

  • 返回值是int 跟strcmp一样
  • 两个指针参数都加上了const ,无法修改内容
  • num是比较的字节数

2、memcmp函数的模拟实现

比较简单,直接上代码
在这里插入图片描述

#include <stdio.h>
#include <assert.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{assert(ptr1 && ptr2);while (num--){if (*(char*)ptr1 == *(char*)ptr2){ptr1 = (char*)ptr1 + 1;ptr2 = (char*)ptr2 + 1;}elsereturn *(char*)ptr1 - *(char*)ptr2;}return 0;
}
int main()
{char ch1[20] = { 0 };char ch2[20] = { 0 };gets(ch1);gets(ch2);int num = 0;scanf("%d", &num);int ret = my_memcmp(ch1, ch2, 5);if (ret > 0)printf(">\n");else if (ret < 0)printf("<\n");elseprintf("==\n");return 0;
}


ok ,这次的分享到这里就结束了,函数的内容基本上就要告一段落了

今天下午还会有一更哦,敬请关注!!!


!!!!!!!!!!!!!!!!!求关注!!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!蹲个一键三连!!!!!!!!!!!!!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/125206.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ-发布订阅模式和路由模式

接上文 RabbitMQ-工作队列 1 发布订阅模式 将之前的配置类内容都替换掉 Bean("fanoutExchange")public Exchange exchange(){//注意这里是fanoutExchangereturn ExchangeBuilder.fanoutExchange("amq.fanout").build();}Bean("yydsQueue1")publ…

行与走,放慢自己,思考回顾。

为什么要出去行与走&#xff1f; 1、出去行与走看到祖国的大美风景&#xff0c;可以更深刻的认识到我们祖国的美好。 2、可以放空心情&#xff0c;排除掉积攒在写字楼内的方格子里面的郁闷和烦恼。 3、可以为自己的身体和心灵找一个安静和清澈净土。 4、在行和走之间&#…

Django基础讲解-路由控制器和视图(Django-02)

一 路由控制器 参考链接&#xff1a; Django源码阅读&#xff1a;路由&#xff08;二&#xff09; - 知乎 Route路由, 是一种映射关系&#xff01;路由是把客户端请求的 url路径与视图进行绑定 映射的一种关系。 这个/timer通过路由控制器最终匹配到myapp.views中的视图函数 …

【C++】vector相关OJ

文章目录 1. 只出现一次的数字2. 杨辉三角3. 电话号码字母组合 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;" 1. 只出现一次的数字 力扣链接 代码展示&#xff1a; class Solution { public:int singleNumber(vector<i…

如何将图片存到数据库(以mysql为例), 使用ORM Bee更加简单

如何将图片存到数据库 1. 创建数据库: 2. 生成Javabean public class ImageExam implements Serializable {private static final long serialVersionUID 1596686274309L;private Integer id;private String name; // private Blob image;private InputStream image; //将In…

【Unet系列】

https://tianfeng.space/1947.html 前言概念 图像分割 分割任务就是在原始图像中逐像素的找到你需要的家伙! 语义分割 就是把每个像素都打上标签&#xff08;这个像素点是人&#xff0c;树&#xff0c;背景等&#xff09; &#xff08;语义分割只区分类别&#xff0c;不区…

案例题--Web应用考点

案例题--Web应用考点 负载均衡技术微服务XML和JSON无状态和有状态真题 在选择题中没有考察过web的相关知识&#xff0c;主要就是在案例分析题中考察 负载均衡技术 应用层负载均衡技术 传输层负载均衡技术 就近的找到距离最近的服务器&#xff0c;并进行分发 使用户就近获取…

git 同时配置 gitee github

git 同时配置 gitee github 1、 删除C:\Users\dell\.ssh目录。 在任意目录右击——》Git Bash Here&#xff0c;打开Git Bash窗口&#xff0c;下方命令在Git Bash窗口输入。 2、添加git全局范围的用户名和邮箱 git config --global user.email "609612189qq.com" …

【应用层协议】HTTPS的加密流程

文章目录 1. 认识HTTPS2. 密文3. HTTPS加密流程3.1 对称加密3.2 非对称加密3.3 证书 1. 认识HTTPS HTTPS&#xff08;超文本传输协议安全&#xff09;也是一个应用层协议&#xff0c;它是在HTTP协议的基础上引入了一个加密层。 也就是HTTP协议传输文本的方式是明文&#xff0c;…

eNSP网络实验

二层VLAN 四台PC的IP地址如图所示&#xff0c;子网掩码均为255.255.255.0&#xff0c;四台PC处在同一个局域网之中&#xff0c;在配置VLAN之前能够彼此ping通。配置的目的是将PC1和PC3划分到VLAN10中&#xff0c;PC2和PC4划分到VLAN20中。 在配置之前需要进入系统视角。 创建V…

Linux CentOS7 vim重复行

在用vim编辑处理文件时&#xff0c;会有重复行。有的是情境需要&#xff0c;有的可能是误操作而形成。对于正常形成的重复行&#xff0c;我们不作讨论&#xff0c;我们仅讨论什么情况下会出现重复行&#xff0c;如何避免&#xff0c;如何处理。 在文件中的单行或多个连续空白行…

lv7 嵌入式开发-网络编程开发 09 UDP通信

目录 1 用到的相关API 1.1 write/read到send/recv 1.2 sendto与recvfrom 2 UDP通信的实现过程 3 服务端代码、客户端、makefile代码实现 1 用到的相关API 1.1 write/read到send/recv send函数原型&#xff1a; ssize_t send(int sockfd, const void *buf, size_t len, …