初识C语言·内存函数

目录

1 memcpy的使用和模拟实现

2 memmove的使用和模拟实现

3 memset的使用和模拟实现

4 memcmp的使用和模拟实现


1 memcpy的使用和模拟实现

紧接字符串函数,出场的是第一个内存函数memcpy。前面讲的字符串函数是专门干关于字符串的事的,而这个函数可以干strcpy一样的事,但是区别就是它碰到\0也会继续复制。

函数的头文件是string,返回类型是void*,参数有两个,一个是目的地地址,一个是源的地址,还有一个是整型,这个整型代表的是要复制多少个字节,返回值是目的地的地址,因为源的值是不能被修改的,所以用const修饰,因为参数是void*,所以在一会儿模拟实现的时候我们就要强制转化成char*的,毕竟是修改字节。

先看一段简单的代码。

int main()
{
	int arr1[10] = { 1,2,9,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, &arr1[4], 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

这串代码的意思就是从arr1[4]开始给arr2复制,复制9个字节,还是很好理解的,那如果不是在一个空间呢?比如我把arr1[4]的位置给arr1开始复制,会不会报错呢?答案是可能会。

如果目的地和源的内存有任何重叠的话,复制的结果都是未定义的,也就是说mencpy的行为是不可预测的,咱也不知道它会干啥,像这样。

int main()
{
	char str[] = "Hello, World!";
	memcpy(str + 7, str, 7);
	printf("%s\n", str);
	return 0;
}

结果是未知的,所以重叠的部分就不应该用mencpy了。

好了,我们现在就模拟实现一下memcpy函数。

主函数开始,传两个地址和一个整型过去,因为复制的是字节,所以我们可以使用字节数当作循环变量,for while循环都可以,因为参数都是泛型指针,所以有必要强制转化为char*指针,转化之后就是复制了,这里有个需要注意的点就是不能直接*(char*)p1++,这样系统会报错的,我们就可以换一个思路,如下。

当然,返回的地址需要用一个临时变量存起来,最后返回就行了,因为返回的是泛型指针,所以临时变量的指针类型也是void*。

void my_memcpy(void* p2, const void* p1, size_t num)
{
	void* ret = p2;
	for ( ; num > 0; num--)
	{
		*(char*)p2 = *(char*)p1;
		(char*)p2 = (char*)p2 + 1;
		(char*)p1 = (char*)p1 + 1;
	}
	return ret;
}
int main()
{
	srand((unsigned int)time(NULL));
	int arr1[10] = { 1,2,9,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int num = rand() % 40 + 1;
	my_memcpy(arr2, arr1, num);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

读者可以自行实验一下。


2 memmove的使用和模拟实现

memcpy是不能让同一块空间复制的,但是menmove就可以,它和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

先上代码实验一下。

int main()
{
	char arr[] = "Hello world!";
	memmove(arr, arr + 1, 3);
	printf("%s", arr);
	return 0;
}

最后的结果就是elllo world!,内存重叠了在memmove这里是没有任何问题的,但是在vs里面尝试对重叠的空间使用memcpy可能是不会报错的,这与vs有关,我们先不用在意。

现在讨论模拟实现memmove。

我们首先想为什么使用内存函数需要考虑空间是否重叠,这是因为如果重叠了就会导致内存复制的时候复制过去上一次复制留下的元素,那么解决方案是什么?是单独拿一块空间出来存储要存放的元素吗?这个实际上可以,但是不免会浪费挺多空间。较好的办法就是改变复制的顺序,如下。

假定红色区域是来源,绿色和蓝色部分是目的地,均有重叠,因为重叠无非就是前面重叠或者是后面重叠,所以有两种情况

第一种情况,绿色情况,假如从前往后复制,即从5开始复制是没有问题的,因为重叠部分5复制的时候9没有发生改变,如果是从后往前复制,即从9开始复制,那么最后复制5的时候,5已经发生了改变,复制就会出错。

第二种情况,蓝色情况,假如从前往后复制,即从5开始复制,那么在复制13那个位置的时候。9已经发生了改变,变成了5,所以复制会出错,同理,后往前复制就不会出错。

可以总结一个小规律:复制的方向取决于重叠的部分,只需要保证在复制的时候,重叠的部分还没有被复制就行,所以一共就两种情况。

void my_memmove(void* p1, const void* p2, size_t num)
{
	void* ret = p2;
	if (p1 < p2)//前往后
	{
		while (num)
		{
			*(char*)p1 = *(char*)p2;
			(char*)p1 = (char*)p1 + 1;
			(char*)p2 = (char*)p2 + 1;
			num--;
		}
	}
	else//后往前
	{
		(char*)p2 = (char*)p2 + num - 1;
		(char*)p1 = (char*)p1 + num - 1;
		while (num)
		{
			*(char*)p1 = *(char*)p2;
			(char*)p1 = (char*)p1 - 1;
			(char*)p2 = (char*)p2 - 1;
			num--;
		}
	}
	return ret;
}
int main()
{
	char str1[100] = "Hello world!";//Hehellworld
	my_memmove(str1 + 2, str1, 4);
	printf("%s", str1);
	return 0;
}

我们讨论的就是传过去的地址的大小是谁高,如果目的地的地址高于来源的地址,那么复制的方向就是从前往后,如果低于,那么复制的地址就是从后往前。特别要注意的是第二种情况,如果是从后往前,那么两个指针指向的位置都需要发生改变,所以需要先加上复制的字节数。第二种情况下,容易犯错的是指向的位置应该加字节数在建减一个1,这点可以自行实验一下。

memmove函数模拟实现就完成了。

可以这样理解,

memcpy可以实现的memmove都可以实现,唯一的区别只是内存空间不能重叠的问题。


3 memset的使用和模拟实现

在cplusplus中,memset的返回值void*,返回的是传进去的地址,参数有三个,泛型指针,存进去的值,设置的字节数。

这个函数的功能就是把内存中的值设置成自己想要的值,那有人问了,为什么第二个参数是int类型的,这是因为字符在计算机中实际上是以AscII值的形式读取的,所以参数类型是int类型。

int main()
{
	char arr1[100] = "abcdefg";
	memset(arr1,'*',6);
	printf("%s", arr1);
	return 0;
}

这是一个简单应用,那么现在模拟实现一下,模拟实现难度不大,每个字节设置一下就行了。

void* my_memset(void* p1, int tem, size_t num)
{
	void* ret = p1;
	while (num--)
	{
		*(char*)p1 = tem;
		(char*)p1 = (char*)p1 + 1;
	}
	return ret;
}
int main()
{
	char arr1[100] = "abcdefg";
	my_memset(arr1, '*', 3);
	printf("%s", arr1);
	return 0;
}

4 memcmp的使用和模拟实现

根据cplusplus,memcmp的返回值是int类型的,实际上和strcmp一样,返回的值就是1 0 -1,strcmp是用来比较字符串的,memcmp就是用来比较内存的,比较的每个字节每个字节的比较的。

头文件还是string,参数有3个,分别是两块内存的地址和比较的字节数的大小,话不多说,看看简单的使用。

int main()
{
	char str1[100] = "abCdefg";
	char str2[100] = "abcdefg";
	int ret1 = memcmp(str1,str2,3);
	int ret2 = memcmp(str2, str1, 3);
	int ret3 = memcmp(str1, str2, 2);
	printf("%d\n", ret1);
	printf("%d\n", ret2);
	printf("%d\n", ret3);
	return 0;
}

所以字符串函数能做到的许多内存函数也是可以做到的,模拟实现和strcmp很是相似,这里就不模拟实现了,重点是memmove的使用和模拟实现。


感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用程序控制微信发送消息

简介 使用杨中科老师的nuget包NetAutoGUI&#xff0c;控制微信给指定用户发送消息&#xff0c;如果想下面视频一样使用此功能用来轰炸朋友&#xff0c;可以直接跳到最后一节&#xff0c;或者直接下载我的打包好的程序集 【免费】控制微信发送消息的程序资源-CSDN文库 微信轰炸…

蓝桥杯备赛 | 洛谷做题打卡day5

蓝桥杯备赛 | 洛谷做题打卡day5 图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~ 文章目录 蓝桥杯备赛 | 洛谷做题打卡day5图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~【深基18.例3】查找文献题目描述 输入格式输出格式样例…

《如何制作类mnist的金融数据集》——1.数据集制作思路

1&#xff0e;数据集制作思路&#xff08;生成用于拟合金融趋势图像的分段线性函数&#xff09; 那么如何去制作这样的一个类minist的金融趋势曲线数据集呢&#xff1f; 还是如上图所示&#xff0c;为了使类别平均分布&#xff0c;因此可以选取三种“buy”的曲线、三种“sell”…

Web前端 ---- 【Vue3】computed计算属性和watch侦听属性(侦听被ref和reactive包裹的数据)

目录 前言 computed watch watch侦听ref数据 ref简单数据类型 ref复杂数据类型 watch侦听reactive数据 前言 本文介绍在vue3中的computed计算属性和watch侦听属性。介绍watch如何侦听被ref和reactive包裹的数据 computed 在vue3中&#xff0c;计算属性computed也是组合式…

C语言天花板——指针(经典题目)

指针我们已经学习的差不多了&#xff0c;今天我来给大家分享几个经典的题目&#xff0c;来让我们相互学习&#x1f3ce;️&#x1f3ce;️&#x1f3ce;️ int main() {int a[4] { 1, 2, 3, 4 };int* ptr1 (int*)(&a 1);int* ptr2 (int*)((int)a 1);printf("%x,%…

Java重修第六天—面向对象3

通过学习本篇文章可以掌握如下知识 1、多态&#xff1b; 2、抽象类&#xff1b; 3、接口。 之前已经学过了继承&#xff0c;static等基础知识&#xff0c;这篇文章我们就开始深入了解面向对象多态、抽象类和接口的学习。 多态 多态是在继承/实现情况下的一种现象&#xf…

随笔03 笔记整理

图源&#xff1a;文心一言 关于我的考研与信息安全类博文整理~&#x1f95d;&#x1f95d; 第1版&#xff1a;整理考研类博文~&#x1f9e9;&#x1f9e9; 第2版&#xff1a;提前列出博文链接&#xff0c;以便小伙伴查阅~&#x1f9e9;&#x1f9e9; 第3版&#xff1a;整理We…

学习记录-自动驾驶与机器人中的SLAM技术

以下所有内容均为高翔大神所注的《自动驾驶与机器人中的SLAM技术》中的内容 融合导航 1. EKF和优化的关系 2. 组合导航eskf中的预测部分&#xff0c;主要是F矩阵的构建 template <typename S> bool ESKF<S>::Predict(const IMU& imu) {assert(imu.timestamp…

基于杂交PSO算法的风光储微网日前优化调度(MATLAB实现)

微网中包含&#xff1a;风电、光伏、储能、微型燃气轮机&#xff0c;以最小化电网购电成本、光伏风机的维护成本、蓄电池充放电维护成本、燃气轮机运行成本及污染气体治理成本为目标&#xff0c;综合考虑&#xff1a;功率平衡约束、燃气轮机爬坡约束、电网交换功率约束、储能装…

Elasticsearch_8.11.4_kibana_8.11.4_metricbeat_8.11.4安装及本地部署_ELK日志部署

文章目录 Elasticsearch_8.11.4_kibana_8.11.4_metricbeat_8.11.4安装及本地部署_ELK日志部署分布式引擎Elasticsearch_8.11.4安装及本地部署系统环境要求1 Windows 安装 Elasticsearch下载完成后进行解压,进入 bin 目录,找到elasticsearch.bat脚本文件执行一键启动.启动都选允…

【Python学习】Python学习15-模块

目录 【Python学习】Python学习15-模块 前言创建语法引入模块from…import 语句from…import* 语句搜索路径PYTHONPATH 变量-*- coding: UTF-8 -*-导入模块现在可以调用模块里包含的函数了PYTHONPATH 变量命名空间和作用域dir()函数globals() 和 locals() 函数reload() 函数Py…

ROS2学习笔记一:安装及测试

目录 前言 1 ROS2安装与卸载 1.1 安装虚拟机 1.2 ROS2 humble安装 2 ROS2测试 2.1 topic测试 2.2 小海龟测试 2.3 RQT可视化 2.4 占用空间 前言 ROS2的前身是ROS&#xff0c;ROS即机器人操作系统&#xff08;Robot Operating System&#xff09;,ROS为了“提高机器人…

原生js实现拖拽效果

<!DOCTYPE html> <html> <head> <style> #mydiv { width: 200px; height: 200px; background-color: red; position: absolute; cursor: move; } </style> | </head> <body> <div id"mydiv">拖拽我…

基于java web的机票管理系统设计与实现设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

机器学习扩散模型简介

一、说明 扩散模型的迅速崛起是过去几年机器学习领域最大的发展之一。在这本易于理解的指南中了解您需要了解的有关扩散模型的所有信息。 扩散模型是生成模型&#xff0c;在过去几年中越来越受欢迎&#xff0c;这是有充分理由的。仅在 2020 年代发布的几篇开创性论文就向世界…

socket.io分房间交流

基本详情看这里 Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信. 效果展示 安装依赖 // 后端插件安装 npm i socket.io -S // 前端插件安装 npm i socket.io-client -S 前端搭建及逻辑 <script setup> import { ref, onMounted…

机器学习 | 卷积神经网络

机器学习 | 卷积神经网络 实验目的 采用任意一种课程中介绍过的或者其它卷积神经网络模型&#xff08;例如LeNet-5、AlexNet等&#xff09;用于解决某种媒体类型的模式识别问题。 实验内容 卷积神经网络可以基于现有框架如TensorFlow、Pytorch或者Mindspore等构建&#xff…

Vue2脚手架配置教程IDEA配置VUE

5.12.3 Vue Cli 文档地址: https://cli.vuejs.org/zh/ IDEA 打开项目&#xff0c;运行项目

React 原理

函数式编程 纯函数 reducer 必须是一个纯函数&#xff0c;即没有副作用的函数&#xff0c;不修改输入值&#xff0c;相同的输入一定会有相同的输出不可变值 state 必须是不可变值&#xff0c;否则在 shouldComponentUpdate 中无法拿到更新前的值&#xff0c;无法做性能优化操作…

Linux网络服务部署yum仓库

目录 一、网络文件 1.1.存储类型 1.2.FTP 文件传输协议 1.3.传输模式 二、内网搭建yum仓库 一、网络文件 1.1.存储类型 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff0…