C语言内存函数
目录
- memcpy(Copy block of memory)使用和模拟实现
- memcpy的模拟实现
- memmove(Move block of memory)使用和模拟实现
- memmove的模拟实现:
- memset(Fill block of memory)函数的使用
- 扩展
- memcmp(Compare two blocks of memory)函数的使用
感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒个人主页
🥸🥸🥸C语言
🐿️🐿️🐿️C语言例题
🐣🐓🏀python
memcpy(Copy block of memory)使用和模拟实现
代码格式:
void * memcpy ( void * destination, const void * source, size_t num );
memcpy使用要点:
1:函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置
2:这个函数在遇到 ‘\0’ 的时候并不会停下来
3:如果source和destination有任何的重叠,复制的结果都是未定义的
4:由于不知道程序猿会传入什么样的指针,所以我们用void * 表示各种类型的指针,因此我们用void*后可以传入int * ,char * …类型的指针
此外我们还有区分 strcpy和memcpy,一个是拷贝字符,一个是拷贝内存,memcpy拷贝是针对不重叠的拷贝)
我们解释一下重叠的含义:
int main()
{int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };memcpy(arr+2, arr , 16);return 0;
}
这就是拷贝重叠,arr+2是表示的&a[2],arr是数组名表示首元素的地址,我们用一个图来说明
首先我们将a[0]=0,a[1]=1分别替换a[2]=2,a[3]=3,由于传进去的是地址,所以是永久改变,此时我们的a[2]和a[3]都发生了变化,但是a[2]和a[3]还没有去替换a[4]和a[5]
当我们用a[2]和a[3]去替换a[4]和a[5]时,a[2]和a[3]已经变成了0和1,所以替换a[4]和a[5]的结果也是0和1
只不过有些编译器的结果并不是这样的(比如VS),因为VS中的memcpy约等于memmove,唯一的解释就是程序猿在实现memcpy时的编写不同,所以代码的结果就不同,但是大多数编译器都是前面的结果
代码示例:
#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]);}return 0;
}
代码解析:
这里有两个int类型的数组,arr2中的元素全是0,我们将arr1中的元素复制到arr2中
由于我们传入的arr2和arr1都是数组的数组名,也就是数组首元素的地址,传入的字节数为20,因为一个数组中的元素大小为4个字节,所以相当于传入5个元素到arr2中
之后就从arr2中第一个元素0开始改变,总共改变5个元素,打印的结果如下
memcpy的模拟实现
void* my_memcpy(void* dest, const void* src, size_t sz)
{assert(dest && src);while (sz--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return dest;
}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]);}return 0;
}
代码解析:
因为是拷贝的内存,但是数组中的元素是整形元素,如果我们拷贝的内存大小不是4的倍数(一个整形大小为4个字节),比如7,那么可能就无法拷贝进去
因此我们需要将传入的数组元素地址进行强制类型转换成char,只有这样才能一个字节一个字节的拷贝, * (char)dest = * (char*)src是将dest和src强制转换为char*类型的指针,再解引用,将src的一个字节拷贝给dest**
而dest = (char)dest + 1和src = (char)src + 1都是将两个数组强制类型转换,再通过+1跳到下一个字节,这样我们就可以保证每一个字节都能够拷贝**
错误代码示例1:
void* my_memcpy(void* dest,const void* src, size_t sz)
{assert(dest && src);while (sz--){*(char*)dest = *(char*)src;(char*)dest++;(char*)src++;}return dest;
}
错误代码示例2:
void* my_memcpy(void* dest,const void* src, size_t sz)
{assert(dest && src);while (sz--){*(char*)dest = *(char*)src;dest = ++(char*)dest ;src = ++(char*)src ;}return dest;
}
错误1具体原因其实我也没有搞懂,错误2只能在某些编译器下可以正常运行,但不是全部,因此就算作错误
memmove(Move block of memory)使用和模拟实现
代码格式:
void * memmove ( void * destination, const void * source, size_t num );
memmove使用要点:
1:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
2:如果源空间和目标空间出现重叠,就得使用memmove函数处理
代码示例:
#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]);}return 0;
}
代码解析:
这里只有一个数组arr1,我们传入的地址为arr1+2和arr1,分别表示数组第三个元素的地址(&arr1[3]),和数组首元素的地址
memmove的模拟实现:
在实现之前我们先理解一下思路:
memmove的实现其实有一种比较简单的方法,就是我们只需要创造一个和arr一模一样的数组brr,由于是两个不同的数组,内存地址是不同的,只是数组元素是相同的,这样我们就解决重叠的问题了
但是我们能不能只通过arr自身而不去创造brr去实现memmove呢?
如图:我们需要将绿色方框中的元素拷贝到红色方框中的元素,可以像这样拷贝,我们先将a[4]=3拷贝到a[6]=5中,然后将a[3]=2拷贝到a[5]=4中…这样就可以避免重叠拷贝,造成拷贝元素不是我们想要的情况了
而如果我们需要将图中绿色方框中的元素拷贝到前面红色方框的元素呢?
其实方式都是一样的
综上如果我们只用数组arr而不创造数组brr的话,我们需要考虑到两种情况,一个是往前拷贝,一个是往后拷贝,所以针对情况我们就一定会用到if语句
具体实现代码如下:
void* my_memmove(void* dest, const void* src, size_t sz)
{assert(dest && src);void* ret = dest;//记录dest启始地址if (dest < src)//从前向后{for (int i = 0; i < sz; i++){*(char*)dest = *(char*)src;dest = (char*)+1;src = (char*)src + 1;}}else//从后向前{while (sz--){*((char*)dest + sz) = *((char*)src + sz);}}return ret;
}
代码解析:
* (char * )dest = * (char*)src是将dest和src强制转换为char*,方便后续可以一个字节一个字节的拷贝
dest = (char*)+1和src = (char*)src + 1是将两个数组都强转后向后移动一个字节
由于while(sz–)是先判断sz是否=0,判断完后再执行后置减减,然后在通过 *((char *)dest + sz) = * ((char * )src + sz)确定需要拷贝的数组和要拷贝数字的位置,我们用一个图来说明:
我们需要将绿色方框中的元素拷贝进红色方框中去,下面是各元素的16进制表示
memset(Fill block of memory)函数的使用
代码格式:
void * memset ( void * ptr, int value, size_t num );
memset使用要点:
1:memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容
代码示例:
#include <stdio.h>
#include <string.h>
int main()
{char str[] = "hello world";memset(str, 'x', 6);printf("%s",str);return 0;
}
代码解析:
memset中我们传入的
扩展
memset是以字节为单位设置内存的,为了方便理解我们看一个代码
int main()
{int arr[10] = { 0 };memset(arr, 1, 40);for (int i = 0; i < 10; i++){printf("%d\n", arr[i]);
}return 0;
}
运行结果如图:
我们调试看一下内存
这是以16进制的方式存储的,在还没有执行memset时内存存储的每个字节都是0
而执行memset后我们可以看到每隔一个字节都会有一个0变成1,因此我们通过这个就比较容易理解以字节为单位设置内存这句话了
所以memset最好还是设置char类型的数组
memcmp(Compare two blocks of memory)函数的使用
代码格式:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
memcmp使用要点:
1:比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
2:memcmp是比较两个内存块
返回值如下
代码示例:
int main()
{int arr1[] = { 1,2,3,4,5,6,7 };//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00...int arr2[] = { 1,2,3 };//01 00 00 00 02 00 00 00 03 00 00 00int a = memcmp(arr1, arr2, 12);printf("%d", a);return 0;
}
代码结果如下:
但如果我们稍微改一下代码呢?
int main()
{int arr1[] = { 1,2,3,4,5,6,7 };//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00...int arr2[] = { 1,2,3,0x11223304 };//01 00 00 00 02 00 00 00 03 00 00 00 04 33 22 11int a = memcmp(arr1, arr2, 13);printf("%d", a);return 0;
}
可以看到我们将arr2中添加了一个16进制的元素0x11223304 ,并且在memcmp中我们将比较的12个字节改为13个字节,结果如下
结果是0,从这里我们也可以看出,memcmp也是将数组中的元素用16进制来表示,再通过一个字节一个字节的比较,方式有点像前面的memset
相关文章:

C语言内存函数
目录 memcpy(Copy block of memory)使用和模拟实现memcpy的模拟实现 memmove(Move block of memory)使用和模拟实现memmove的模拟实现: memset(Fill block of memory)函数的使用扩展 memcmp(Compare two blocks of memory)函数的使用 感谢各位大佬对我的支持,如果我的文章对你有…...

【Docker】Docker-compose及Consul多容器编排工具
使用一个Dockerfile模版文件可以定义一个单独的应用容器,当需要定义多个容器时就需要编排 docker swarm(管理跨节点) 编排工具——docker compose Dockerfile可以让用户管理一个单独的应用容器;而Compose则允许用户在一个模板&…...

Unity网络同步方案帧同步和状态同步
网络同步方案 介绍开始我们使用的状态同步(实时状态同步)后来采用的帧同步 状态同步优点缺点 帧同步顺序执行追帧重连优点缺点 总结 这两年做的都是帧同步和状态同步的项目,正好最近有时间总结一下什么是帧同步和状态同步,之前在做…...

【Monorepo实战】pnpm+turbo+vitepress构建公共组件库文档系统
Monorepo架构可以把多个独立的系统放到一起联调,本文记录基于pnpm > workspace功能,如何构建将vitepress和组件库进行联调,并且使用turbo进行任务顺序编排。 技术栈清单: pnpm 、vitepress 、turbo 一、需求分析 1、最终目标…...

CentOS 编译安装Redis
一、编译配置hiredis.h C来操作redis数据库。通过hiredis接口来实现,目前只能在Linux环境使用。 下载hiredis.h hiredis的下载地址为:https://github.com/redis/hiredis 解压并编译hiredis [rootlocalhost source_code]# pwd /usr/local/source_…...

可拓展的低代码全栈框架
尽管现在越来越多的人开始对低代码开发感兴趣,但已有低代码方案的局限性仍然让大家有所保留。其中最常见的担忧莫过于低代码缺乏灵活性以及容易被厂商锁定。 显然这样的担忧是合理的,因为大家都不希望在实现特定功能的时候才发现低代码平台无法支持&…...

C++11 智能指针
目录 智能指针 异常导致执行流乱跳 智能指针解决问题 auto_ptr unique_ptr sharded_ptr weak_ptr 智能指针 由于C11引入异常之后,执行流乱跳,所以导致之前 malloc/new 的空间很容易没有被释放,导致内存泄露问题。 所以这时候&#x…...

二、WebGPU阶段间变量(inter-stage variables)
二、WebGPU阶段间变量(inter-stage variables) 在上一篇文章中,我们介绍了一些关于WebGPU的基础知识。在本文中,我们将介绍阶段变量(inter-stage variables)的基础知识。 阶段变量在顶点着色器和片段着色…...

【Linux】31个普通信号
文章目录 1.每种信号的含义2.两种不能被忽略的信号3.两种不能被捕捉的信号 1.每种信号的含义 信号编号信号名信号含义1SIGHUP如果终端接口检测到一个连接断开,则会将此信号发送给与该终端相关的控制进程,该信号的默认处理动作是终止进程。2SIGINT当用户…...

Mac电脑交互式原型设计 Axure RP 8汉化最新 for mac
Axure RP 8是一款专业且快速的原型设计工具,主要用于定义需求、规格、设计功能和界面。这款工具主要适用于用户体验设计师、交互设计师、业务分析师、信息架构师、可用性专家和产品经理等职业。 Axure RP 8的主要特性包括能够快速设计出应用软件或Web网站的线框图、…...

在线免费无时长限制录屏工具 - 录猎在线版
需要录屏的小伙伴注意啦,想要长时间录制又不想花钱的,可以看下这款在线版录屏软件 —— 录猎在线版,一个录屏软件所需要的基本功能它都有,设置录制范围、录制的声音来源、摄像头也能录制的。同时它是支持Windows和Mac系统的&#…...

c语言文件操作详解:fgetc,fputc,fgets,fputs,fscanf,,fprintf,fread,fwrite的使用和区别
前言:在对于c语言的学习中,我们为了持续使用一些数据,为了让我们的数据可以在程序退出后仍然保存并且可以使用,我们引入了文件的概念和操作,本文旨在为大家分享在文件操作中常用的输入输出函数的使用方式和技巧&#x…...

Harmony装饰器
1、装饰器 装饰器是用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如: Component表示自定义组件Entry表示该自定义组件为入口组件State表示组件中的状态变量,状态变量变化会触发UI刷新。 2 、语法范式 Builder/BuilderParam&#…...

如何加快Chrome谷歌浏览器下载速度?
用Chrome打开chrome://flags/...

使用kubectl连接远程Kubernetes(k8s)集群
使用kubectl连接远程Kubernetes集群 环境准备下载kubectl下载地址 安装kubectl并处理配置文件Windows的安装配置安装kubectl拉取配置文件安装kubectl拉取配置文件kubectl命令自动补全 Linux的安装配置安装kubectl拉取配置文件kubectl命令自动补全 环境准备 你需要准备一个Kube…...

Kubernetes革命:云原生时代的应用编排和自动化
文章目录 什么是Kubernetes以及为何它备受欢迎?云原生应用和K8s的关系Kubernetes的核心概念:Pods、Services、ReplicaSets等部署、扩展和管理应用程序的自动化容器编排的演进:Docker到Kubernetes实际用例:企业如何受益于K8s的应用…...

mysql.mongoDb,neo4j数据库对比
#Mysql与MongoDb和Neo4j的一些对比 主要区别 MySQL: 1.MySQL是一种关系型数据库管理系统(RDBMS),广泛用于处理结构化数据。 2.它支持SQL语言,具备成熟的事务处理和数据一致性能力。 3.MySQL适用于大多数传统的基于表…...

unity使用UniStorm 5.1.0.unitypackage增加天气
添加天天气组件unistorm 然后添加一个player 导入包会报错,需要修改代码 using UnityEngine; using UnityEngine.PostProcessing;namespace UnityEditor.PostProcessing {[CustomPropertyDrawer(typeof(UnityEngine.PostProcessing.MinAttribute))]sealed class MinDrawer : …...

Flink实现kafka到kafka、kafka到doris的精准一次消费
1 流程图 2 Flink来源表建模 --来源-城市topic CREATE TABLE NJ_QL_JC_SSJC_SOURCE ( record string ) WITH (connector kafka,topic QL_JC_SSJC,properties.bootstrap.servers 172.*.*.*:9092,properties.group.id QL_JC_SSJC_NJ_QL_JC_SSJC_SOURCE,scan.startup.mode …...

Outlook屏蔽Jira AI提醒
前言:最近不知道为什么jira上的ai小助手抽风,一周发个几千封邮件…导致我现在都不想在邮箱里面跟找垃圾一样找消息了。实在忍无可忍,决定屏蔽AI小助手,方法很简单,follow me~~ 第一步:双击打开电脑版Outloo…...

毛玻璃 has 选择器卡片悬停效果
效果展示 页面结构 从上述的效果展示可以看到,页面是由多个卡片组成,并且鼠标悬停在卡片上时,会旋转用户图片并且韩式对应的用户信息框。 CSS3 知识点 :has 属性的运用 实现页面整体结构 <div class"container"><div…...

[hive]解决group by 字段超过系统规定64个
用开窗函数即可 ( row_number() over(partition by col1,...,col70 oder by xx) rn ) where rn1...

生成老年人的声音sox
sox laoren1.wav laoren2.wav pitch -300...

DC2DC电源设计注意事项--1,Feedback
电源采集图如下图 Feedback 采集电压点应该在靠近负载侧。这样可以减少大电流导线导致的电压差,真实反应输出电压值 FB_1P21采集电路靠近芯片侧, 2.1,采集分压电路上侧为Vout Vnoise, 那么一分压就噪声就小了。假如采集电路远离芯片侧&…...

计算机视觉处理的开源框架
计算机视觉是一门涉及图像和视频分析的领域,有许多开源的框架和库可用于构建计算机视觉应用程序。以下是一些常见的计算机视觉开源框架及其特点,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合…...

最新AI智能创作系统源码AI绘画系统/支持GPT联网提问/支持Prompt应用
AI绘图专业设计 不得将程序用作任何违法违纪内容,不要让亲人两行泪 界面部分图解构: 前台show: 前端部署: 安装pm2管理器 点击设置 选择v16.19.1版本-切换版本 再新建一个网站 点击设置 添加反向代理-代理名称随便…...

2019架构真题案例(四十八)
系统应用集成构件统一标准的基础平台,在各个应用系统的接口之间数据共享和功能,基本原则是保证应用程序的()。系统应用集成提供了四个不同层次的服务,最上层服务是()。 独立性相关性互操作性排…...

zabbix自定义监控内容和自动发现
6 目录 一、自定义监控内容: 1.明确需要执行的 linux 命令 2.创建 zabbix 的监控项配置文件,用于自定义 key: 3. 在 Web 页面创建自定义监控项模板: 3.1 创建模板: 3.2 创建监控项: 3.3 创建触发器&#…...

导引服务机器人 通用技术条件
声明 本文是学习GB-T 42831-2023 导引服务机器人 通用技术条件. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 6 检验规则 6.1 检验项目 检验分为型式检验和出厂检验。检验项目见表2。 表 2 检验项目 序号 检验项目 技术要求 检验方法 出厂检验 型…...

今日头条文章采集ChatGPT3.5/4.0驱动浏览器改写文章软件说明文档
大家好了,我是淘小白~ 今天给大家介绍的软件是一个款驱动浏览器改写文章的软件,下面给大家做一下介绍说明: 一、软件语言 Python编写的,使用的库是selenium库 二、具体逻辑 1、整理头条文章网址,需要自己整理&…...