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…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...

如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...

Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...