【C语言】模拟实现内存函数
本篇文章目录
- 相关文章
- 1. 模拟 memcpy 内存拷贝
- 2. 模拟 memmove 内存移动
相关文章
- 【C语言】数据在内存中是以什么顺序存储的?
- 【C语言】整数在内存中如何存储?又是如何进行计算使用的?
- 【C语言】利用void*进行泛型编程
- 【C语言】4.指针类型部分
使用内存库函数实际上要包含string.h头文件,这个大伙要注意。
1. 模拟 memcpy 内存拷贝
两个指针的指向必须是两块互相独立的内存区域,即两个不同的数组。
dest空间必须比src空间大;
bytes表示要从src拷贝到dest的字节数。
// void* 通用的泛型编程,可以接收任何指针
void* my_memcpy(void* dest, const void* src, size_t bytes) {assert(dest && src);if (dest == src) {return dest;}void* t = dest;while (bytes--) {// 不清楚void*接收的是什么类型指针,直接char*一个个字节拷贝。*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;/*((char*)dest)++;((char*)src)++;---------------++((char*)dest);++((char*)src);这两种写法换成c++都不行*/}return t;
}
int main() {int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };int arr2[] = { 1, 2, 3, 4};my_memcpy(arr1 + 4, arr2, sizeof(int) * 4);for (int i = 0; i < 8; i++) {printf("%d ", arr1[i]);}return 0;
}

对于标准的memcpy,如果不进行完善实际上是有问题的:假设有一个数组 int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 },指针dest和指针src两个指针指向同一个数组内存,并且以字节为单位进行内存移动,会出现几种情况:
1.dest和src指向同一个地址,如my_memcpy(arr, arr, 4),等于啥也没动;2.dest指向地址值大于src2.1 如my_memcpy(arr + 4, arr, 16),也就是将5 6 7 8改成1 2 3 4,结果为1 2 3 4 1 2 3 4;-----------------------------------------------------2.2 如my_memcpy(arr + 2, arr, 16),也就是将3 4 5 6改成1 2 3 4,即 1 2 1 2 3 4 7 8但结果会变成1 2 1 2 1 2 7 8,因为原来3 4位置被改成了1 2。3.dest指向地址值小于src3.1 如my_memcpy(arr, arr + 4, 16),也就是将1 2 3 4改成5 6 7 8,结果为5 6 7 8 5 6 7 8;-----------------------------------------------------3.2 如my_memcpy(arr, arr + 2, 16),也就是将1 2 3 4改成3 4 5 6,结果为 3 4 5 6 5 6 7 8。
到这里会发现,也就只有2.2的结果不是我们想要的,这是因为自己实现的memcpy并不不完善,如果是string.h库函数中的memcpy则不存在这个问题。

本来是要将3 4 5 6改成1 2 3 4,结果改成了1 2 1 2。
事实上对于memcpy函数,C语言标准定义的是两个指针指向的内存位置不能是同一块区域,但显然对于vs2022的编译器而言是将memcpy完善了。但我们使用时还是尽量不要将两个指针指向同一个数组内的元素地址,毕竟要考虑到其它编译器并不一定完善。
画图分析2.2 my_memcpy(arr + 2, arr, sizeof(int) * 4):

图中每个格子代表arr数组中的一个元素,每个元素四个字节。我们利用这个简单的图分析上面模拟实现memcpy的代码,不难看出实际上拷贝是从前往后进行拷贝的,也就是从src、dest的起始位置开始往后拷贝。当拷贝完8个字节后,就变成了下面的样子:

这时的3和4早已被拷贝成了1和2,3和4不存在了。那么5和6就无法被拷贝成3和4了,自然而然也变成了1和2。
而解决这个问题,使用库函数memmove最好,对于这个函数,C语言的使用标准是这样的:两个指针既可以指向同一块内存区域,也可以像memcpy一样,两个指针指向不同内存区域。
2. 模拟 memmove 内存移动
如果自己实现memmove,通过上面例子出现的问题(同一块数组内存区域),如果要解决该问题,要考虑到的情况实际上也就是从前往后还是从后往前拷贝的问题,这个得由dest和src的地址大小比较后决定。
就对于上面模拟实现memcpy的问题,如果是从后往前拷贝,比如把6改成4,再把5改成3,再把3改成2,把2改成1,互不影响那么问题迎刃而解,但如果大伙认为真这么简单那就打错特错了。
对于内存而言以字节为单位,1个整型4个字节,我们实际上是要从最后一个字节开始往前一个个字节拷贝。arr数组的内存布局如下:

每一格都是1个字节,四格则构成一个完整的整型数据,也就是arr数组中的一个元素。格子中的值是用十六进制表示的,至于为什么是倒着存储的,这是因为当前机器以小端字节序存储数据(详细了解请看本篇文章最上面的 相关文章位置,第一个链接中的文章有解释)。
则对于dest地址值大于src的情况,可以这样拷贝:

而对于dest地址值小于src的情况,照常从前往后拷贝:

//模拟memmove(两个指针的指向可以是两块互相独立内存,也可以是同一块内存)
void* my_memmove(void* dest, const void* src, size_t bytes) {assert(dest && src);void* t = dest;// 从后面最后一个字节往前,将6改成4,再将5改成3,4改成2,3改成1,解决上面memcpy 2.2中的问题if (dest > src) {while (bytes--) { *((char*)dest + bytes) = *((char*)src + bytes);}} else if (dest < src) { while (bytes--) {*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}} return t;
}int main() {int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };my_memmove(arr, arr + 2, sizeof(int) * 4);for (int i = 0; i < 8; i++) {printf("%d ", arr[i]);}return 0;
}

成功把3 4 5 6改成1 2 3 4!

把1 2 3 4改成3 4 5 6也没问题!
相关文章:
【C语言】模拟实现内存函数
本篇文章目录 相关文章1. 模拟 memcpy 内存拷贝2. 模拟 memmove 内存移动 相关文章 【C语言】数据在内存中是以什么顺序存储的?【C语言】整数在内存中如何存储?又是如何进行计算使用的?【C语言】利用void*进行泛型编程【C语言】4.指针类型部…...
Jenkins学习笔记3
gitgithubjenkins: 架构图: 说明:jenkins知道github有更新了,就pull进行构建build,编译、自动化测试。然后部署到应用服务器。 maven java的项目构建工具。 在开发者电脑上创建空密码密钥对。 [rootgit-developer ~…...
基于单片机火灾报警器仿真设计
一、系统方案 1、本设计采用51单片机作为主控器。 2、DS18B20采集温度值送到液晶1602显示。 3、MQ2采集烟雾值,送到液晶1602显示。 4、按键设置温度报警值,大于报警值,声光报警。 二、硬件设计 原理图如下: 三、单片机软件设计…...
阿里测开面试大全(一)附答案完整版
万字长文,建议收藏 1 什么是POM,为什么要使用它? POM是Page Object Model的简称,它是一种设计思想,而不是框架。大概的意思是,把一个一个页面,当做一个对象,页面的元素和元素之间操…...
STL-常用容器
string容器 string构造函数 string本质:类 string和char*区别: char* 是一个指针 string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。 特点: string类内部封装了很多成员方法 …...
【owt】关闭microk8s 等无关服务
打算部署下owt,发现之前跑了microk8s ,一直运行:操作指令 // 1. 启动 microk8s.start// 2. 关闭 microk8s.stop// 3. kubectl 操作 // --- 查看 cluster microk8s.kubectl cluster-info// --- 查看 nodes microk8s.kubectl get nodes// --- 查看 pods microk8s.kubectl get …...
【面试题】——Spring
1.Spring是什么? Spring是一个开源的Java应用框架,它提供了广泛的基础设施支持,用于构建Java应用程序。极大提高了开发效率。它提供了一种轻量级的编程模型,通过依赖注入(Dependency Injection)和面向切面…...
【算法思想-排序】根据另一个数组次序排序 - 力扣 1122 题
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...
毕业设计|基于stm32单片机的app视频遥控抽水灭火小车设计
基于stm32单片机的app视频遥控抽水灭火水泵小车设计 1、项目简介1.1 系统构成1.2 系统功能 2、部分电路设计2.1 L298N电机驱动电路设计2.2 继电器控制电路设计 3、部分代码展示3.1 小车控制代码3.1 水泵控制代码 4 演示视频及代码资料获取 1、项目简介 视频简介中包含资料http…...
编译原生安卓aosp源码,实现硬改以及定位
系列文章目录 第一章 安卓aosp源码编译环境搭建 第二章 手机硬件参数介绍和校验算法 第三章 修改安卓aosp代码更改硬件参数 第四章 编译定制rom并刷机实现硬改(一) 第五章 编译定制rom并刷机实现硬改(二) 第六章 不root不magisk不xposed lsposed frida原生修改定位 第七章 安卓…...
找单身狗。一个数组中只有两个数字出现一次,其他数字出现了两次,编写一个函数找出这两个只出现一次的数字
例:在{1 2 3 4 5 6 1 2 3 4}找出5和6 方法二: 设计思想: 1.分组原理 (1)将所有数字进行异或,相同数字异或为零,所以只会剩5^6,即为异或的结果xor_result (…...
Java数据结构技巧
Java数据结构技巧 1、循环 for-each循环如果不是"[]"的数组类型,则需要提前判断数据结构是否为空,否则有可能会有空指针异常。 2、对于List对象的i到j位进行排序 for(List<String> now_result:result){List<String> sublist …...
easyui disabled 属性设置
1.设置disabled $("#id").attr("disabled",true); 或 $("#id").attr("disabled","随意字符"); easyui写法 $("#id").numberbox("textbox").attr("disabled", true); $("#id")…...
使用容器运行Nginx应用及Docker命令
目录 一、使用容器运行Nginx应用 1.1 使用docker run命令运行Nginx应用 1.1.1 观察下载容器镜像过程 1.1.2 观察容器运行情况 编辑 1.2 访问容器中运行的Nginx服务 1.2.1 确认容器IP地址 1.2.2 容器网络说明 1.2.3 在主机中使用curl命令容器IP地址访问 二、Docker命…...
fastapi 基本介绍+使用
FastAPI是一个基于Python 3.6的现代、快速(高性能)的web框架,它使用Starlette作为其底层Web框架。FastAPI有很好的文档和丰富的功能,包括自动为路由生成API文档、查询参数验证、依赖注入、WebSocket等等。 以下是一个FastAPI的基…...
C语言的结构体的认识
注:类似于①、②……是代码的编写顺序,也是对下方代码的注解 【①】、【②】……是用到了之前的代码 #include <stdio.h> //②定义生日结构体,必须声明在前面不然会报错,c语言是从上到下执行的(这点要注意&#…...
只通过在vimrc文件写东西来实现或安装vim的插件
2023年9月23日,周日上午 有时候觉得用插件管理器来安装插件太麻烦了, 所以我就在想能不能只通过在vimrc文件写东西来实现或安装vim的插件, 不过这样做肯定有很大的局限性,但我会尽量做到最好的效果 不定期更新 把下面这些代码…...
云原生Kubernetes:K8S存储卷
目录 一、理论 1.存储卷 2.emptyDir 存储卷 3.hostPath卷 4.NFS共享存储 5.PVC 和 PV 6.静态创建PV 7.动态创建PV 二、实验 1.emptyDir 存储卷 2.hostPath卷 3.NFS共享存储 4.静态创建PV 5.动态创建PV 三、问题 1.生成pod一直pending 2.shoumount -e未显示共享…...
“五育”并举育人体系构建的实践研究课题实施方案
目录 一、研究背景与意义 二、课题理论依据 三、国内外研究情况与现状 四、研究目标...
小样本目标检测:ECEA: Extensible Co-Existing Attention for Few-Shot Object Detection
论文作者:Zhimeng Xin,Tianxu Wu,Shiming Chen,Yixiong Zou,Ling Shao,Xinge You 作者单位:Huazhong University of Science and Technology; UCAS-Terminus AI Lab 论文链接:http://arxiv.org/abs/2309.08196v1 内容简介: 1&…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
