函数扩展之——内存函数
前言:小伙伴们又见面啦。
本篇文章,我们将讲解C语言中比较重要且常用的内存函数,并尝试模拟实现它们的功能。
让我们一起来学习叭。
目录
一.什么是内存函数
二.内存函数有哪些
1.memcpy
(1)库函数memcpy
(2)模拟实现memcpy
2.memmove
(1)库函数memmove
(2)模拟实现memmove
3.memset
4.memcmp
四.总结
一.什么是内存函数
我们从这个名字不难看出,这将会是一个函数,还是一个对内存进行操作的函数。
而事实上,这其实也是一种对数据进行操作的函数。
我们之前学习过:strcmp、strcpy、strlen等等,它们都是对字符进行操作的函数,统称为字符串函数。但是这些字符串函数却有着弊端,那就是它们仅仅只能操作字符串,而像整型这些其他类型的数据却无法操作。
因此我们引出了内存函数,帮助我们对各种各样类型的数据进行操作。
二.内存函数有哪些
- memcpy
- memmove
- memset
- memcmp
和字符串操作函数类似,内存函数也是由内存的英文"memory",和后边的操作方式的英文拼接而成,同时使用它们都需要头文件#include<string.h>。
下面我们来逐个讲解这四个内存函数的具体用法。
1.memcpy
和strcpy类似,memcpy也是数据拷贝,将一个数组里的数据拷贝到另一个数组中去。
先来认识一下memcpy的函数头:
这个函数有三个参数:
destination 代表着目的地
source 代表着源头
num 约束着我们要操作的数据数目
有没有小伙伴们知道,为什么数组指针参数的返回值以及函数的返回值都要是void*类型呢???
我们已经知道,memcpy是用来拷贝各种各样的数据类型的函数,那么它的参数就不能是单一的char*或者int*,而void*则充当一个中转站,他可以接收任意类型的数据,也可以在函数内部通过强制类型转换成各种各样的数据类型。
size_t是无符号整型,因为我们拷贝字符串不可能说拷贝负数个或者小数个,所以用它来接收。
而我们的源头source我们是不希望它有任何变化或者被修改的,所以要用一个const来修饰。
事实上我们这篇文章讲到的所有内存函数的参数都是如此。
(1)库函数memcpy
下面我们来看一下memory的具体用法:
#include<stdio.h>
#include<string.h>
int main()
{int arr1[10] = { 0 };int arr2[5] = { 1,2,3,4,5 };memcpy(arr1, arr2, 20);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;}
来看这样一个简单的代码及其结果,我们用memcpy成功的实现了整型数据的拷贝。
这时候有小伙伴们会说:你这个代码不对吧,你memcpy里的数据数目为啥是20啊???
事实上,memcpy的数据数目代表的是字节数,而5个整型的字节数刚好是20。
那为什么是字节数而不是元素的个数呢???
下面我们就通过模拟实现一个memcpy函数来看看它的具体内部构造:
(2)模拟实现memcpy
自主实现memcpy,我们完全可以使用它本身的函数头,也就是:
void* My_memcpy(void* destination, const void* source, size_t num)
下面我们来分析怎么构造函数体:
我们知道,任何类型的数据都有它的大小,字节数基本都不相同,但是它们都有最小的单位,那就是一个字节的char类型。
因此,我们将形参强制类型转换为char*类型,一个字节一个字节的拷贝,是不是就能实现啦。
这时候我们就悟出来了为什么数据数目是字节数了。
下面来看具体函数体实现:
#include<stdio.h>
#include<string.h>
void* My_memcpy(void* destination, const void* source, size_t num)
{char* p = destination;while(num--){*(char*)destination = *(char*)source;destination = (char*)destination + 1;source = (char*)source + 1;}return p;
}
int main()
{int arr1[10] = { 0 };int arr2[5] = { 1,2,3,4,5 };My_memcpy(arr1, arr2, 20);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
按我们前边分析的,将destination和source都转化为char*指针,再用while循环实现一个字节一个字节的拷贝,这样我们便能够实现对一组整型数据的拷贝了。来看结果:
这时候请小伙伴们思考一个问题,我们上边实现的是将一个数组的数据拷贝到另一组数组中,
那我们能不能在一个数组的内部进行拷贝呢???
比如说我现在有一个数组:
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
我现在要将1,2,3,4,5拷贝到3,4,5,6,7的位置去,得到1,2,1,2,3,4,5,8,9,10能否实现???
事实上是不行的:
当我们第一步把1拷贝到3的位置时,3的值已经被替换成1,整个数据变成了1,2,1,4,5,6,7,8,9,10。
会导致我们得到1,2,1,2,1,2,1,8,9,10 这样一个结果。
这时候我们的memcpy就无法实现我们想要的结果了,于是我们引出了memmove函数。
(这里要补充一点,不同的编译器memcpy的功能不完全相同,比如博主所使用的VS2019的memcpy就可以实现同一个数组元素的拷贝,有的却不行)
2.memmove
memmove的函数头和memcpy一模一样,只是函数体有些许不同。
下面我们先来看作为库函数的memmove的使用。
(1)库函数memmove
#include<stdio.h>
#include<string.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr + 2, arr, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
值得注意的一点是,我们知道数组名在一般情况下代表的是数组的首元素地址,因此我们向函数传递时,分别传递我们要进行操作的两个数列的首元素的地址便可。
例如我们上述就是将数组的1-5位拷贝到3-7位,所以就将第一位和第三位的地址作为参数传过去。
得到结果为:
那到底怎么才能实现同一个数组内数据的相互拷贝呢???
先来分析一下,既然我们从前往后会造成值被修改,那我们从后往前可不可以呢???
例如我们还是将1,2,3,4,5拷贝到3,4,5,6,7上去,先将5拷贝到7,再将4拷贝到6,这样下去,确实能够完成。
下面我们来模拟实现一下memmove的具体内部构造。
(2)模拟实现memmove
#include<stdio.h>
#include<string.h>
void* My_memmove(void* destination, const void* source, size_t num)
{char* p = destination;while (num--){*((char*)destination + num) = *((char*)source + num);}return p;
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };My_memmove(arr + 2, arr, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
要注意的是,既然是从后往前一个字节一个字节的拷贝,那么我的destination和source指针都需要指向末位,所以在解引用之前要先加上num。
这样我们便实现了数组内部的拷贝。
但是这时候问题又来了,如果我想将3,4,5,6,7拷贝到1,2,3,4,5上去,从后往前还管用吗???
显然,又出现被覆盖的情况,看来,我们上边的代码还有缺陷,没有完全实现memmove的功能。
那该怎么解决呢???
这时候我们思考一下,将前边的数据拷贝到后边要从后往前,相对的将后边的数据拷贝到前边是不是要从前往后,那我们将这两者整合在一起不就好了。
只需要一个判断条件,判断是将前边的数据拷贝到后边还是将后边的数据拷贝到前边不就行啦。
现在我们只需要解决一个问题,怎么判断呢???
其实这个很简单,如果是将前边的数据拷贝到后边,那么源头的地址就会比目的地小,反之,将后边的数据拷贝到前边,那么源头的地址就会比目的地小。
这时候我们只需要比较一下源头和目的地的地址就好啦。
来看具体实现:
#include<stdio.h>
#include<string.h>
void* My_memmove(void* destination, const void* source, size_t num)
{char* p = destination;if (destination < source){while (num--){*(char*)destination = *(char*)source;destination = (char*)destination + 1;source = (char*)source + 1;}}else{while (num--){*((char*)destination + num) = *((char*)source + num);}}return p;
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };My_memmove(arr, arr + 2, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
这样我们便实现memmove的完整功能啦。
看到这里,小伙伴们是不是都感觉非常的累,其实博主我讲到这里也是非常的累。
事实上对于上边的两个函数,我们更需要掌握他们的内部构造,而接下来要讲的剩下的两个函数就比较简单了,我们只需要知道怎么用它们就可以啦。
3.memset
这个函数叫做内存设置函数,其作用是修改内存中的若干个数据。
ptr 是我们要修改的数据起始位置
value 是我们要改为的数据
num 是我们要修改的数据数量
来看实操:
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "hello world";memset(arr, '*', 5);printf("%s", arr);return 0;
}
我们要将"hello"五个字符全改为"*",便将数组首地址,"*",5作为参数传入,得到结果如下:
值得注意的是,这个函数也是只能一个字节一个字节的操作。
#include<stdio.h>
#include<string.h>
int main()
{int arr[5] = {0};memset(arr, 1, 20);return 0;
}
假如我要把这个整型数组的五个元素都改为1,一个字节一个字节的改要改20个,所以我们传入20,但是我们来看结果和内存:
这并不是我们想要的结果,一个字节8个bite位,所以我们实际上得到的是二进制序列:
00000001 00000001 00000001 00000001,也就是十进制的16843009。
所以这个函数可以说是只能跟char型的数据挂钩啦,不要轻易用在其他类型哦。
4.memcmp
既然是cmp结尾,肯定也是一个比较函数,是一个内存比较函数。
但是这个比较函数有点特殊,它可以让你指定要比较的位置和数量,可以说是strcmp pro max。
这里值得注意的是,这个函数的返回值类型是int,当前者 > 后者时,返回 1,反之返回 -1,相等则返回0。
来看具体使用:
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[] = { 1,2,3,4,5 };int ret = memcmp(arr1 + 1, arr2, 20);printf("%d", ret);return 0;
}
(arr1 + 1)便跑到了2的位置,2 > 1,自然会返回1。
使用这个函数值得注意的一点是,它只会比较第一组遇见的不相同的数据,而不会比较整体的数据和的大小。
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[] = { 1,2,4,1,1 };int ret = memcmp(arr1, arr2, 20);printf("%d", ret);return 0;
}
例如上述代码,1 + 2 + 3 + 4 + 5 明显大于1 + 2 + 4 + 1 + 1,但是只因为3 < 4就返回了 - 1。
四.总结
终于终于,内存函数的讲解到这里就结束啦!!!
感谢各位小伙伴能够耐心的看到最后,希望博主的讲解能够帮助到你们。
本篇创作实属不易,不要忘记支持努力的博主呀,记得一键三连哦!!!
我们下期再见啦!!!
相关文章:
函数扩展之——内存函数
前言:小伙伴们又见面啦。 本篇文章,我们将讲解C语言中比较重要且常用的内存函数,并尝试模拟实现它们的功能。 让我们一起来学习叭。 目录 一.什么是内存函数 二.内存函数有哪些 1.memcpy (1)库函数memcpy &…...
【在线机器学习】River对流数据进行机器学习
River是一个用于在线机器学习的Python库。它旨在成为对流数据进行机器学习的最用户友好的库。River是crme和scikit-multiflow合并的结果。 https://github.com/online-ml/river 举个简单示例,将训练逻辑回归来对网站网络钓鱼数据集进行分类。下面介绍了数据集中的…...
第 4 章 串(串的块链存储实现)
1. 背景说明 该实现和链表的实现极为相似,只是将链接的内存拆分为具体的大小的块。 2. 示例代码 1). status.h /* DataStructure 预定义常量和类型头文件 */#ifndef STATUS_H #define STATUS_H#define CHECK_NULL(pointer) if (!(pointer)) { \printf("FuncN…...
Element表格之表头合并、单元格合并
一、合并表头 el-table配置 :header-cell-style"headFirst"headFirst({ row, colunm, rowIndex, columnIndex }) {let base { background-color: rgba(67, 137, 249, 0.3), color: #333, text-align: center };//这里为了是将第一列的表头隐藏,就形成了合…...
go学习-JS的encodeURIComponent转go
背景 encodeURIComponent() 函数通过将特定字符的每个实例替换成代表字符的 UTF-8 编码的一个、两个、三个或四个转义序列来编码 URI(只有由两个“代理”字符组成的字符会被编码为四个转义序列)。 与 encodeURI() 相比,此函数会编码更多的字…...
MySQL索引、事务与存储引擎
索引 事务 存储引擎 一、索引1.1 索引的概念1.2 索引的实现原理1.2 索引的作用1.3 创建索引的依据1.4 索引的分类和创建1.4.1 普通索引 index1.4.2 唯一索引 unique1.4.3 主键索引 primary key1.4.4 组合索引(单列索引与多列索引)1.4.5 全文索引 fulltex…...
【Spring面试】八、事务相关
文章目录 Q1、事务的四大特性是什么?Q2、Spring支持的事务管理类型有哪些?Spring事务实现方式有哪些?Q3、说一下Spring的事务传播行为Q4、说一下Spring的事务隔离Q5、Spring事务的实现原理Q6、Spring事务传播行为的实现原理是什么?…...
Windows平台Qt6中UTF8与GBK文本编码互相转换、理解文本编码本质
快速答案 UTF8转GBK QString utf8_str"中UTF文"; std::string gbk_str(utf8_str.toLocal8Bit().data());GBK转UTF8 std::string gbk_str_given_by_somewhere"中GBK文"; QString utf8_strQString::fromLocal8Bit(gbk_str_given_by_somewhere.data());正文…...
【探索Linux】—— 强大的命令行工具 P.9(进程地址空间)
阅读导航 前言一、内存空间分布二、什么是进程地址空间1. 概念2. 进程地址空间的组成 三、进程地址空间的设计原理1. 基本原理2. 虚拟地址空间 概念 大小和范围 作用 虚拟地址空间的优点 3. 页表 四、为什么要有地址空间五、总结温馨提示 前言 前面我们讲了C语言的基础知识&am…...
ESP32主板-MoonESP32
产品简介 Moon-ESP32主板,一款以双核芯片ESP32-E为主芯片的主控板,支持WiFi和蓝牙双模通信,低功耗,板载LED指示灯,引出所有IO端口,并提供多个I2C端口、SPI端口、串行端口,方便连接,…...
Python 图片处理笔记
import numpy as np import cv2 import os import matplotlib.pyplot as plt# 去除黑边框 def remove_the_blackborder(image):image cv2.imread(image) #读取图片img cv2.medianBlur(image, 5) #中值滤波,去除黑色边际中可能含有的噪声干扰#medianBlur( Inp…...
SpringCloud Ribbon--负载均衡 原理及应用实例
😀前言 本篇博文是关于SpringCloud Ribbon的基本介绍,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力…...
Redis的介绍以及简单使用
Redis(Remote Dictionary Server)是一个开源的内存数据存储系统,它以键值对的形式将数据存在内存中,并提供灵活、高性能的数据访问方式。Redis具有高速读写能力和丰富的数据结构支持,可以广泛应用于缓存、消息队列、实…...
ad18学习笔记十二:如何把同属性的元器件全部高亮?
1、先选择需要修改的器件的其中一个。 2、右键find similar objects,然后在弹出的对话框中,将要修改的属性后的any改为same 3、像这样勾选的话,能把同属性的元器件选中,其他器件颜色不变 注意了,如果这个时候ÿ…...
SpringSecurity 核心过滤器——SecurityContextPersistenceFilter
文章目录 前言过滤器介绍用户信息的存储获取用户信息存储用户信息获取用户信息 处理逻辑总结 前言 SecurityContextHolder,这个是一个非常基础的对象,存储了当前应用的上下文SecurityContext,而在SecurityContext可以获取Authentication对象…...
反转单链表
思路图1: 代码: struct ListNode* reverseList(struct ListNode* head){if(headNULL)//当head是空链表时 {return head; }struct ListNode* n1NULL;struct ListNode* n2head;struct ListNode* n3head->next;if(head->nextNULL)//当链表只有一个节…...
加速新药问世,药企如何利用云+网的优势?
随着计算能力的不断提高和人工智能技术的迅速发展,药物研发领域正迎来一场革命。云端强大的智能算法正成为药物研发企业的得力助手,推动着药物的精确设计和固相筛选。这使得药物设计、固相筛选以及药物制剂开发的时间大幅缩短,有望加速新药物…...
C++中string对象之间比较、char*之间比较
#include <cstring> //char* 使用strcmp #include <string> //string 使用compare #include <iostream> using namespace std; int main() {string stringStr1 "42";string stringStr2 "42";string stringStr3 "213";cout …...
MVVM模式理解
链接: MVVM框架理解及其原理实现 - 知乎 (zhihu.com) 重点: 1.将展示的界面窗口和创建的构件是数据进行分离 2.利用一个中间商进行数据的处理,所有的数据通过中间商进行处理...
js常用的数组处理方法
some 方法 用于检查数组中是否至少有一个元素满足指定条件。如果有满足条件的元素,返回值为 true,否则返回 false。 const numbers [1, 2, 3, 4, 5];const hasEvenNumber numbers.some((number) > number % 2 0); console.log(hasEvenNumber); /…...
[Document]VectoreStoreToDocument开发
该document是用来检索文档的。 第一步:定义组件对象,该组件返回有两种类型:document和text。 第二步:获取需要的信息,向量存储库,这里我使用的是内存向量存储(用该组件拿到文档,并检…...
【LeetCode-简单题】225. 用队列实现栈
文章目录 题目方法一:单个队列实现 题目 方法一:单个队列实现 入栈 和入队正常进行出栈的元素其实就是队列的尾部元素,所以直接将尾部元素弹出即可,其实就可以将除了最后一个元素的其他元素出队再加入队,然后弹出队首元…...
数据预处理方式合集
删除空行 #del all None value data_all.dropna(axis1, howall, inplaceTrue) 删除空列 #del all None value data_all.dropna(axis0, howall, inplaceTrue) 缺失值处理 观测缺失值 观测数据缺失值有一个比较好用的工具包——missingno,直接传入DataFrame&…...
【前端】jquery获取data-*的属性值
通过jquery获取下面data-id的值 <div id"getId" data-id"122" >获取id</div> 方法一:dataset()方法 //data-前缀属性可以在JS中通过dataset取值,更加方便 console.log(getId.dataset.id);//112//赋值 getId.dataset.…...
GB28181学习(五)——实时视音频点播(信令传输部分)
要求 实时视音频点播的SIP消息应通过本域或其他域的SIP服务器进行路由、转发,目标设备的实时视音频流宜通过本域的媒体服务器进行转发;采用INVITE方法实现会话连接,采用RTP/RTCP协议实现媒体传输;信令流程分为客户端主动发起和第…...
单例模式(饿汉模式 懒汉模式)与一些特殊类设计
文章目录 一、不能被拷贝的类 二、只能在堆上创建类对象 三、只能在栈上创建类对象 四、不能被继承的类 五、单例模式 5、1 什么是单例模式 5、2 什么是设计模式 5、3 单例模式的实现 5、3、1 饿汉模式 5、3、1 懒汉模式 🙋♂️ 作者:Ggggggtm &#x…...
133. 克隆图
133. 克隆图 题目-中等难度示例1. bfs 题目-中等难度 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。 图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。…...
交流耐压试验目的
试验目的 交流耐压试验是鉴定电力设备绝缘强度最有效和最直接的方法。 电力设备在运行中, 绝缘长期受着电场、 温度和机械振动的作用会逐渐发生劣化, 其中包括整体劣化和部分劣化,形成缺陷, 例如由于局部地方电场比较集中或者局部…...
使用 YCSB 和 PE 进行 HBase 性能压力测试
HBase主要性能压力测试有两个,一个是 HBase 自带的 PE,另一个是 YCSB,先简单说一个两者的区别。PE 是 HBase 自带的工具,开箱即用,使用起来非常简单,但是 PE 只能按单个线程统计压测结果,不能汇…...
正则表达式相关概念及不可见高度页面的获取
12.正则 概念:匹配有规律的字符串,匹配上则正确 1.正则的创建方式 构造函数创建 // 修饰符 igm// i 忽视 ignore// g global 全球 全局// m 换行 var regnew RegExp("匹配的内容","修饰符")var str "this is a Box";var reg new RegExp(&qu…...
idc 网站备案/制作网站软件
#参考资料大型分布式网站架构设计与实践 #感悟:书读百遍,其意自见 #QQ群北京it—推荐–交流:300458205 #群专注内推、大数据、云计算、Java、Android、UI等技术交流,欢迎你的加入。 看的越多,发现会的越少ÿ…...
男鞋 东莞网站建设/加盟培训机构
Python的PyQt框架的使用-常用控件篇一、前言二 、QLineEdit 文本框三 、QPushButton按钮控件四、QRadioButton 单选按钮一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡,小伙伴们,让我们一起来学习Python的PyQt框架的常用控件。如果文章对你有帮助、欢迎…...
好看的网站页面/深圳经济最新新闻
1.冯诺依曼体系结构 输入设备:键盘,网卡等输出设备:显示器等存储器:进行中间数据缓冲运算器:进行数据运算控制器:进行设备控制 所有的设备都是围绕存储器工作的(CPU控制器运算器)&am…...
惠州专业网站制作公司/seo营销技巧
中国银联是经同意,批准设立的中国银行卡组织。成立以来,顺应国家社会经济发展和人民群众用卡需要,牢记历史使命,履行社会责任,充分发挥银行卡组织的职能作用,推动我国银行卡产业实现了快速、健康发展&#…...
如何做淘宝客个人网站/semantic scholar
Open Replicator ( http://code.google.com/p/open-replicator/ ) 开源了。Open Replicator是一个用Java编写的MySQL binlog分析程序。Open Replicator 首先连接到MySQL(就像一个普通的MySQL Slave一样),然后接收和分析binlog,最终…...
阜阳建设大厦网站/竞价推广价格
angularjs使用如果您还没有尝试过Angular,那么您会错过为什么人们说JavaScript是世界上最灵活的语言的原因。 Angular是唯一不会使MVC看起来像在猪上涂口红的框架。 如今,大多数框架只是现有工具的捆绑。 它们是一个集成的工具集,但不是很优…...