C语言常用的内存函数
在上一篇博客中我为大家分享了一些常用的字符串函数,以及它们的用法和模拟实现。通过字符串函数中的strcpy,我们能够做到将一个字符串中的内容拷贝到另一个字符串上,可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢?这样看来好像strcpy就不适用了,因为它只能对字符串进行拷贝。那么今天所提到的内存函数就能够实现整型数组,甚至其他类型的拷贝,让我们开始今天的学习吧~!
一、memcpy函数
我们可以看到,memcpy函数的返回类型以及参数类型大多都是void类型,这也就使得它能够接收不同类型的数据,并且能够对不同类型的数据加以处理~
①memcpy函数的使用
memcpy函数的作用是:将num字节的值从源指向的位置直接复制到目标指向的内存块。由于参数为void*类型,所以源指针和目标指针指向的对象的底层类型与此函数无关。(注:这个函数在遇到 '\0' 的时候并不会停下来。)
memcpy函数的第一个参数void* destination指向目标起始位置,代表想要改变的初始位置,第二个参数const void* source指向想要复制的数据的起始位置,它的作用是作为模板为目标复制字节,第三个参数size_t表示要复制的字节数。那么了解了memcpy函数的组成,让我们举个例子,尝试一下使用memcpy函数拷贝整型数组吧~
int main()
{int arr0[20] = { 0 };int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr0, arr1, 10 * sizeof(int));for (int i = 0; i < 10; i++){printf("%d ", arr0[i]);}return 0;
}
甚至我们还能做到使用memcpy拷贝结构体
struct stu
{char name[20];int age;long id;
};
int main()
{struct stu s0 = { "xiaowang",20,10666 };struct stu s1 = { "xiaosong",40,10333 };memcpy(&s0, &s1, 8 * sizeof(char));memcpy(&s0, &s1, 7 * sizeof(int));printf("%s\n", s0.name);printf("%d\n", s0.age);printf("%ld\n", s0.id);return 0;
}

(我这里只是举一个例子,对于结构体的拷贝还需要学会结构体内存对齐,如果有机会的话下次我会单独用一篇博客来分享一下关于结构体内存对齐的知识)
②memcpy函数的模拟实现
不知道小伙伴们还记不记得我们之前学习过的qsort函数,让我们顺便回顾一下吧,qsort函数可以排序任意类型的数据,函数底层使用的是快速排序的方法。那么qsort函数是怎么做到能够排序任意类型数据的呢?没错,就是在不知道数据类型的情况下直接交换每一个字节。
而我们的memcpy函数也差不多是这个意思,我们最后传递的size_t类型参数就代表了需要替换的字节数,所以我们可以传递一个size_t类的num代表字节数,使用一个while循环,每循环一次,传递的num就减一,直到num为0时循环结束,此时正好交换了num个字节。
void* my_memcpy(void* arr0, const void* arr1, size_t num)
{assert(arr0 && arr1);while (num--){*(char*)arr0 = *(char*)arr1;arr0 = (char*)arr0 + 1;arr1 = (char*)arr1 + 1;}return (void*)arr1;
}
int main()
{int arr0[20] = { 5,5,5,5,5 };int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };printf("替换前:\n");for (int i = 0; i < 5; i++){printf("%d ", arr0[i]);}my_memcpy(arr0, arr1, 5 * sizeof(int));printf("\n替换后:\n");for (int i = 0; i < 5; i++){printf("%d ", arr0[i]);}return 0;
}
那我们能不能尝试自己拷贝自己,做到将arr1里的4,5,6,7,8拷贝成它的前五位元素1,2,3,4,5呢?让我们尝试一下:
void* my_memcpy(void* arr0, const void* arr1, size_t num)
{assert(arr0 && arr1);while (num--){*(char*)arr0 = *(char*)arr1;arr0 = (char*)arr0 + 1;arr1 = (char*)arr1 + 1;}return (void*)arr1;
}
int main()
{int arr0[20] = { 5,5,5,5,5 };int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };printf("替换前arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}my_memcpy(arr1+3, arr1, 5 * sizeof(int));printf("\n替换后arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
可以看到在我们想用arr1前方的元素拷贝给后方时,却打印出了错误的答案,这是为什么呢?因为在对arr1进行拷贝的时候改变的是arr1本身,当第一次拷贝时arr1中的4变成了1,第二次拷贝时5变成了2,第三次拷贝时6变成了3,第四次拷贝时,我们需要将7变成4,可是此时arr1中的4已经被替换成了1,所以此时7变成了1,同理8也变成了2。这就说明了:(memcpy函数j仅仅是简单的从前往后进行拷贝,并没有考虑内存有重叠的情况,如果内存有重叠,其行为是不确定的。)
那么此时,下一个要介绍的函数就该登场了~
二、memmove函数
可以看到memmove的参数与memcpy函数是一致的,但与memcpy函数相较,memmove函数能够完美的处理重叠的情况。
①memmove函数的使用
• 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
• 如果源空间和目标空间出现重叠,就可以使用memmove函数处理。
那么我们使用memmove来证实上面的想法,就用刚刚memcpy打印出错的代码:
int main()
{int arr0[20] = { 5,5,5,5,5 };int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };printf("替换前arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}memmove(arr1+3, arr1, 5 * sizeof(int));printf("\n替换后arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
看来这次使用memmove确实解决了重叠的情况。
②memmove函数的模拟实现
想要实现memmove函数的模拟实现,就需要我们解决上面memcpy函数的重叠问题。那么memcpy函数的重叠问题是如何造成的呢?
刚刚的思路我们可以用这张图来表示,而我们按照memcpy的思路来进行拷贝就是从前往后拷贝,用图片来表示出来应该是这样的:
这就是出现了重叠的情况,我们想调用arr[3]时,其指向的元素已经从4变成了1,想要避免这种情况我们只需要在拷贝元素时保证需要拷贝的内容不被替换就好了。就比如此时,我们转换一下思路,我们现在想将1,2,3,4,5拷贝成4,5,6,7,8。这样的话能不能做到呢?是可以的!因为我们在将1拷贝成4后,拷贝的位置和需要拷贝的内容的位置都向后挪一位,此中重叠的元素为4和5,但4和5在最开始就为1和2拷贝成功啦,所以并不会发生错误的打印。这种情况就是拷贝内容初始位置在拷贝初始位置之后,此时需要使用顺序拷贝。
而对于上面将4,5,6,7,8变成1,2,3,4,5,我们可以尝试用逆序拷贝。
通过这个图可以知道,拷贝内容初始位置在拷贝初始位置之前,此时需要使用逆序拷贝。通过逆序拷贝的方式,就能够避免在拷贝前,拷贝内容被替换掉的情况。
想要模拟实现memmove函数,我们只需要将两种情况结合在一起,先判断两个初始位置谁在前谁在后,然后再对应的编写出解决方案就可以啦~(拷贝内容初始位置在拷贝初始位置之后时,直接使用memcpy的模拟函数就可以,拷贝内容初始位置在拷贝初始位置之前时,就逆序拷贝,从需要拷贝的最后一个字节往前拷贝就能够解决了)
void* my_memmove(void* arr0, const void* arr1, size_t num)
{assert(arr0 && arr1);if (arr0 < arr1){while (num--){*(char*)arr0 = *(char*)arr1;arr0 = (char*)arr0 + 1;arr1 = (char*)arr1 + 1;}}else{while (num--)*((char*)arr0 + num) = *((char*)arr1 + num);}return (void*)arr1;
}
int main()
{int arr0[20] = { 5,5,5,5,5 };int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };printf("替换前arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}my_memmove(arr1 + 3, arr1, 5 * sizeof(int));printf("\n替换后arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
这样就成功的模拟实现了memmove函数啦~
三、memset函数
memset的作用是:复制字符 value (注意,不是字符串,是一个字符)到参数 ptr 所指向的字符串的前 num 个字符。
memset和前两个函数最大的区别就是,前两个函数可以传输的是可以每一个的不同的,可以改变的,多样的。而memset能做到的只是将一个固定的字符拷贝到一段空间中。那么它的具体用法是什么呢?我们来尝试一下使用memset将一段数组进行清零。
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };printf("清零前arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}memset(arr1, 0, 10 * sizeof(int));printf("\n清零后arr1:\n");for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
能够看到这边也是成功清零了,那可能有人会问,那不是也可以把值全部变成1,全部变成2之类的吗?那让我们来尝试一下这个思路:
欸?怎么出现了这么一堆又奇怪又大的数字,这是怎么一回事呢?请仔细看刚刚的介绍,我们传输的并不是直接的一个值,而是一个将参数转化为二进制后填入的一个字节,比如此时我们想填入的是1,那么1转换为二进制形式就是0000 0001,但是arr中存放的int整形变量有四个字节,所以传进四个字节后其实这个对应的值会变成0000 0001 0000 0001 0000 0001 0000 0001。也就是一个非常大的数字。
由于之前传递的0对应的就是00 00 00 00所以会直接是0,并不是说明传递的是什么数字,就会是什么数字,想要理解memset函数,就要清楚这一点。而传递其他的值大部分比较麻烦,所以memset函数的主要作用还是使结构体或数组进行清零(注意并不是说就没有其他的作用了哦)。
四、memcmp函数

memcmp的作用是:比较从ptr1和ptr2指针指向的位置开始,向后的num个字节。
memcmp的用法与strncmp的用法很像,都是用于指定长度的比较大小,并且都是大于返回>0的数,小于返回<0的数,等于返回0。唯一不同的是memcmp函数是内存函数,通过它能够比较所有的类型,因为不论存储的内容是字符串,整数还是其它类型的数据,该函数都会逐个字节进行比较。
int main()
{int arr0[] = { 1,2,3,4,5,6 };int arr1[] = { 1,2,3,4,5,7 };if (memcmp(arr0, arr1, 6 * sizeof(int)) > 0){printf("arr0>arr1");}else if (memcmp(arr0, arr1, 6 * sizeof(int)) < 0){printf("arr0<arr1");}else{printf("arr0=arr1");}return 0;
}
由此就可以进行两个数组的大小的比较。同样字符串也是可以的:
int main()
{char arr0[] = "abcdef";char arr1[] = "abcdez";if (memcmp(arr0, arr1, 6 * sizeof(char)) > 0){printf("arr0>arr1");}else if (memcmp(arr0, arr1, 6 * sizeof(char)) < 0){printf("arr0<arr1");}else{printf("arr0=arr1");}return 0;
}
那么关于比较常用的内存函数的知识,就给大家分享到这里啦,如果有讲解的有问题或者不清楚的地方,还希望各位在评论区多多指出,我也会吸取教训,多多学习的,那么我们下期再见啦~
相关文章:
C语言常用的内存函数
在上一篇博客中我为大家分享了一些常用的字符串函数,以及它们的用法和模拟实现。通过字符串函数中的strcpy,我们能够做到将一个字符串中的内容拷贝到另一个字符串上,可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢&#…...
MP4 H.264 MPEG-4 MPEG-2
MP4 视频编解码技术 H.264 MPEG-4 MPEG-2 MP4 (MPEG-4 Part 14): Format: A digital multimedia container format.Use: Often used to store video, audio, subtitles, and still images.Compression: Can use different codecs, such as H.264, for video compression. H.264…...
nvm 切换、安装 Node.js 版本
nvm下载路径 往下拉找到Assets 下载后,找到nvm-setup.exe双击,一直无脑下一步,即可安装成功。 配置环境变量(我的是window11) 打开任务栏设置–搜环境变量 配置好后,点确定一层一层关闭 windowR 打开控制…...
基于区块链的合同存证应用开发
基于区块链的合同存证应用开发 任务一:环境准备 1.启动区块链网络 目录: /root/xuperchain/output/ 启动区块链网络 bash constrol.sh start2.创建钱包账户 目录: /root/xuperchain/output/ 创建tenant, landlord钱包账户,命令如下: bin/xchain-cli account newke…...
每日一题~ abc 365 E 异或运算(拆位+贡献)
处理位运算常用的方法: 拆位法(一位一位的处理,通常题目中会给出元素的最大是2的的多少次幂,当然也有给10的次幂的,自己注意一下就可以了) 常用的思想 : 算贡献。 异或的性质: A^A0 …...
前端八股文笔记【三】
JavaScript 基础题型 1.JS的基本数据类型有哪些 基本数据类型:String,Number,Boolean,Nndefined,NULL,Symbol,Bigint 引用数据类型:object NaN是一个数值类型,但不是…...
AI学习记录 - transformer的Embedding层
创作不易,免费的赞 前面有介绍了GPT2如何进行token化的过程,现在讲下transformer的Embedding层 Embedding层就是一个巨大的矩阵,边长分别是词汇表长度和词向量维度,矩阵里面的每一个数字都是一个随机初始化的,或者是…...
23-PCB封装名称的统一添加与管理
1.进入封装管理器 2. 选择对象,点击右侧添加按钮 3. 搜索所需要的封装 4.接受创建变更 5.执行变更 6.关闭...
【Python从入门到进阶】62、Pandas中DataFrame对象案例实践
接上篇《61、Pandas中DataFrame对象的操作(二)》 上一篇我们讲解DataFrame对象的统计分析、可视化以及数据导出与保存相关内容。本篇我们延续之前学习的DataFrame对象的知识,结合一个数据案例进行实践操作。 一、案例说明 我们将通过一个股…...
使用Python实现深度学习模型:智能环境监测与预警
介绍 智能环境监测与预警是保护生态环境和人类健康的重要手段。通过深度学习技术,我们可以实时获取环境数据,分析环境变化趋势,及时发出预警。本文将介绍如何使用Python和深度学习库TensorFlow与Keras来构建一个简单的环境监测与预警模型。 环境准备 首先,我们需要安装必…...
ThreadLocal的使用场景是什么
ThreadLocal 是 Java 中用于实现线程局部变量的工具,它提供了每个线程独立的变量副本,使得不同线程对该变量的操作不会相互干扰。以下是 ThreadLocal 的常见使用场景: 线程安全的对象共享: ThreadLocal 可以用来避免线程间共享状…...
【网络爬虫篇】逆向实战—某东:滑块验证码(逆向登录)2024.8.7最新发布,包干货,包详细
【网络爬虫篇】更多优秀文章借鉴: 1. 使用Selenium实现黑马头条滑块自动登录 2. 使用多线程采集爬取豆瓣top250电影榜 3. 使用Scrapy爬取去哪儿网游记数据 4. 数据采集技术综合项目实战1:国家水稻网数据采集与分析 5. 数据采集技术综合项目实战2&#x…...
为什么优质的酱香白酒都会带点苦味?
大家好,我是酱酒亮哥,不知大家有没有发现,在制作一杯美味的咖啡或是烘焙一块香脆的面包时,制作过程中都会有一些独特的味道和香气产生,对吧?同样地,酱香白酒的酿造过程也是一个复杂而精细的化学…...
软件测试常见面试题
软件测试阶段分为单元测试,集成测试,系统测试,验收测试。单元测试策略为对代码中的函数方法进行测试,目的是发现代码的问题。集成测试策略是模块中组合起来进行测试,要求发现与接口有关的问题。系统测试策略是子系统的…...
面试经典算法150题系列-接雨水
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,…...
【C++】 类型转换深度探索:揭开类型转换的奥秘
🌈 个人主页:Zfox_ 🔥 系列专栏:C从入门到精通 目录 一: 🚀 C语言中的类型转换 二: 🔥 为什么C需要四种类型转换 三: 🔥 C强制类型转换 🥝 3.1 st…...
深入探索Webkit的Web Authentication API:安全与便捷的融合
Web Authentication API,通常被称为WebAuthn,是一个新兴的Web标准,旨在通过提供更安全、更便捷的认证方式来改善用户的在线体验。随着Webkit对WebAuthn的支持日益增强,本文将深入探讨这一API的功能、实现方式以及如何在Webkit浏览…...
Vue - 关于v-wave 波浪动画组件
Vue - 关于v-wave 波浪动画组件 这个动画库可以在标签中添加新的v-wave属性,来让点击标签元素后添加漂亮的波纹效果,并且可以根据父元素自动形成波纹的颜色,也可以自定义波纹颜色,持续时间,透明度,触发的对…...
计算机网络408考研 2019
计算机网络408考研2019年真题解析_哔哩哔哩_bilibili 2019 1 1 1 1...
实时捕捉与追溯:得物基于 eBPF 打造云上网络连接异常摄像头
近期我们容器 SRE 团队基于 eBPF 技术建设网络连接异常感知能力,灰度上线过程中发现了生产环境 10 以上的应用配置错误、程序 Bug 等问题。在和应用负责同学同步风险过程中,大家都挺好奇我们如何实现在对应用无侵入的情况下发现服务连接异常的。本篇文档…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
