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

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

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

相关文章

PSN 两步验证解除2023.10.9经验贴

背景 本人10月1号收到Sony邮件&#xff0c;说是不规律登录&#xff0c;需修改密码后登录&#xff0c;然后我10月8日登录PS4的时候&#xff0c;提示两步验证。当时就想坏了&#xff0c;然后找B站相关经验贴&#xff0c;10月9号电话香港客服&#xff0c;解除了两步验证&#xff0…

Windows10打开应用总是会弹出提示窗口的解决方法

用户们在Windows10电脑中打开应用程序&#xff0c;遇到了总是会弹出提示窗口的烦人问题。这样的情况会干扰到用户的正常操作&#xff0c;给用户带来不好的操作体验&#xff0c;接下来小编给大家详细介绍关闭这个提示窗口的方法&#xff0c;让大家可以在Windows10电脑中舒心操作…

计算机网络八股

1、请你说说TCP和UDP的区别 TCP提供面向连接的可靠传输&#xff0c;UDP提供面向无连接的不可靠传输。UDP在很多实时性要求高的场景有很好的表现&#xff0c;而TCP在要求数据准确、对速度没有硬件要求的场景有很好的表现。TCP和UDP都是传输层协议&#xff0c;都是为应用层程序服…

大数据——Spark Streaming

是什么 Spark Streaming是一个可扩展、高吞吐、具有容错性的流式计算框架。 之前我们接触的spark-core和spark-sql都是离线批处理任务&#xff0c;每天定时处理数据&#xff0c;对于数据的实时性要求不高&#xff0c;一般都是T1的。但在企业任务中存在很多的实时性的任务需求&…

超大视频如何优雅切片

背景 有一次录屏产生了一个大小为33G的文件, 我想把他上传到B站, 但是B站最大只支持4G. 无法上传, 因此做了一个简单的探索. 质疑与思考 a. 有没有一个工具或一个程序协助我做分片呢? 尝试 a. 必剪 > 有大小限制, 添加素材加不进去(而且报错信息也提示的不对) b. PR &…

C++设计模式_07_Bridge 桥模式

文章目录 1. 动机&#xff08;Motivation&#xff09;2. 代码演示Bridge 桥模式2.1 基于继承的常规思维处理2.2 基于组合关系的重构优化2.3 采用Bridge 桥模式的实现 3. 模式定义4. 结构&#xff08;Structure&#xff09;5. 要点总结 与上篇介绍的Decorator 装饰模式一样&…

从零开始读懂相对论:探索爱因斯坦的科学奇迹

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 阿尔伯特爱因斯坦…

竞赛 机器视觉 opencv 深度学习 驾驶人脸疲劳检测系统 -python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#x…

Transformer预测 | Pytorch实现基于Transformer 的锂电池寿命预测(CALCE数据集)

文章目录 效果一览文章概述模型描述程序设计参考资料效果一览 文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t…

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

flutter开发实战-video_player插件播放抖音直播实现&#xff08;仅限Android端&#xff09; 在之前的开发过程中&#xff0c;遇到video_player播放视频&#xff0c;通过查看video_player插件描述&#xff0c;可以看到video_player在Android端使用exoplayer&#xff0c;在iOS端…

workerman的基本用法(示例详解)

workerman是什么&#xff1f; Workerman是一个异步事件驱动的PHP框架&#xff0c;具有高性能&#xff0c;可轻松构建快速&#xff0c;可扩展的网络应用程序。支持HTTP&#xff0c;Websocket&#xff0c;SSL和其他自定义协议。支持libevent&#xff0c;HHVM&#xff0c;ReactPH…

el-table 设置最大高度且能刚好撑满

max-height"calc(90vh - 120px)"90vh视口高度的90%自行调整即可

解决: 使用html2canvas和print-js打印组件时, 超出高度出现空白页

如果所示&#xff1a;当我利用html2canvas转换成图片后, 然后使用print-js打印多张图片, 第一张会出现空白页 打印组件可参考这个: Vue-使用html2canvas和print-js打印组件 解决: 因为是使用html2canvas转换成图片后才打印的, 而图片是行内块级元素, 会有间隙, 所以被挤下去了…

真香!Jenkins 主从模式解决问题So Easy~

01.Jenkins 能干什么 Jenkins 是一个开源软件项目&#xff0c;是基于 Java 开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 中文官网&#xff1a;https://jenkins.io/zh/ 0…

Docker基础(CentOS 7)

参考资料 hub.docker.com 查看docker官方仓库&#xff0c;需要梯子 Docker命令大全 黑马程序员docker实操教程 &#xff08;黑马讲的真的不错 容器与虚拟机 安装 yum install -y docker Docker服务命令 启动服务 systemctl start docker停止服务 systemctl stop docker重启…

Redis AOF重写原原理

重写aof之前 appendonly.aof.1.base.aof appendonly.aof.1.incr.aof appendonly.aof.manifest 重写aof 一次 appendonly.aof.2.base.aof 大小变化 appendonly.aof.2.incr.aof 大小o appendonly.aof.manifest 大小不变 AOF文件重写并不是对原文件进行重新整理&#xff0c;而是直…

Docker搭建MySQL8.0主从复制(一主一从)

0. 配置说明 宿主机使用的版本为19045的win10专业版&#xff0c;MySQL使用的是8.0&#xff0c;Docker容器使用Linux。 1. 安装Docker Desktop 略 修改Docker默认安装路径 安装包自己就提供了修改安装路径的功能&#xff0c;CMD中运行&#xff1a; “Docker Desktop Installe…

财务明细一目了然,颜色标记记录轻松掌握个人账目!

无论您是想更好地理清个人收支&#xff0c;还是希望在财务管理中更加高效&#xff0c;我们为您推荐一款绝佳的财政管理神器&#xff1a;颜色标记记录&#xff01; 第一步&#xff0c;首先&#xff0c;我们要先进入【晨曦记账本】主页面&#xff0c;并点击上方功能栏里的“添加…

[开源]MIT协议,开源论坛程序,拥有友好的用户界面和操作体验

一、开源项目简介 尤得一物是一个开源论坛程序&#xff0c;提供丰富的功能&#xff0c;可以作为管理或分享文章的论坛博客&#xff0c;也可以在此基础上进行自定义开发。 二、开源协议 使用MIT开源协议 三、界面展示 四、功能概述 尤得一物是一个开源论坛程序&#xff0c;…

如何使用Docker轻松构建和管理应用程序(一)

如今Docker的使用已经非常普遍&#xff0c;特别在一线互联网公司。使用Docker技术可以帮助企业快速水平扩展服务&#xff0c;从而到达弹性部署业务的能力。在云服务概念兴起之后&#xff0c;Docker的使用场景和范围进一步发展&#xff0c;如今在微服务架构越来越流行的情况下&a…