初识C语言·内存函数

news2025/2/23 3:50:27

目录

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的使用和模拟实现。


感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(十二)EEPROM的补充

文章目录 EEPROM补充篇读EEPROM补充内容写EEPROM补充内容单字节写入多字节拆成单字节写入现象 EEPROM补充篇 读EEPROM补充内容 对于上一篇博文在读EEPROM的时候&#xff0c;提到的DUMMY WRITE&#xff1a; 这里怎么理解呢&#xff1a; 大家看&#xff0c;写EEPROM的逻辑除了…

c++学习笔记-STL案例-演讲比赛管理系统2

目录 功能介绍 代码结构部分 查看一下类图 1.Speaker.h 2.speechManager.h 3.speechManager.cpp 4.演讲比赛流程关系系统.cpp 功能介绍 speechManager.h函数包含演讲比赛流程的所有功能如下&#xff1a; 开始演讲比赛&#xff1a;完成整届比赛的流程&#xff0c;每…

为什么使用双token实现无感刷新用户认证?

单token机制 认证机制&#xff1a;对与单token的认证机制在我们项目中仅使用一个Access Token的访问令牌进行用户身份认证和授权的方案处理。 不足之处&#xff1a; 安全性较低&#xff08;因为只有一个token在客户端和服务器端之间进行传递&#xff0c;一旦Access Token被截…

解决ERROR 24680 --- [ main] o.a.catalina.core.AprLifecycleListener 报错:

1.报错全称&#xff1a; ERROR 24680 --- [ main] o.a.catalina.core.AprLifecycleListener : An incompatible version [1.2.32] of the Apache Tomcat Native library is installed, while Tomcat requires version [1.2.34] 2.解决方案&#xff1a; 步骤一 在…

Linux下编写zlg7290驱动(1)

大家好&#xff0c;今天给大家介绍Linux下编写zlg7290驱动(1)&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 在智能仪表中&#xff0c;经常会用到键盘、数码管等外设。因此&…

LLM之RAG理论(五)| 使用知识图谱增强RAG

知识图谱&#xff08;KG&#xff09;或任何图都包括节点和边&#xff0c;其中每个节点表示一个概念&#xff0c;每个边表示一对概念之间的关系。本文介绍一种将任何文本语料库转换为知识图谱的技术&#xff0c;本文演示的知识图谱可以替换其他专业知识图谱。 一、知识图谱 知识…

【PyTorch简介】3.Loading and normalizing datasets 加载和规范化数据集

Loading and normalizing datasets 加载和规范化数据集 文章目录 Loading and normalizing datasets 加载和规范化数据集Datasets & DataLoaders 数据集和数据加载器Loading a Dataset 加载数据集Iterating and Visualizing the Dataset 迭代和可视化数据集Creating a Cust…

深度解析Pytest插件pytest-html

在软件开发中&#xff0c;测试报告是开发者获取测试结果和问题定位的关键工具之一。然而&#xff0c;标准的控制台输出有时难以满足我们对测试报告的需求。幸运的是&#xff0c;Pytest插件 pytest-html 提供了一种简单而强大的方式&#xff0c;可以生成漂亮、可视化的HTML格式测…

Python之Matplotlib绘图调节清晰度

Python之Matplotlib绘图调节清晰度 文章目录 Python之Matplotlib绘图调节清晰度引言解决方案dpi是什么&#xff1f;效果展示总结 引言 使用python中的matplotlib.pyplot绘图的时候&#xff0c;如果将图片显示出来&#xff0c;或者另存为图片&#xff0c;常常会出现清晰度不够的…

如何开启文件共享及其他设备如何获取

1.场景分析 日常生活中&#xff0c;常常会遇到多台电脑共同办公文件却不能共享的问题&#xff0c;频繁的用移动硬盘、U盘等拷贝很是繁琐&#xff0c;鉴于此&#xff0c;可以在同一内网环境下设置共享文件夹&#xff0c;减少不必要的文件拷贝工作&#xff0c;提升工作效率。废话…

什么是信噪比

大家好&#xff0c;今天给大家介绍什么是信噪比&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 “信噪比”是电子技术中经常用到的一个词组&#xff0c;知道它的确切含义有一定意…

[含完整代码]Linux使用.sh脚本自动部署(启动|停止|状态|日志)项目[超详细]

前言&#xff1a; 个人博客&#xff1a;www.wdcdbd.com 我们在linux部署.jar项目时&#xff0c;都需要通过java -jar的形式来部署&#xff0c;每次都要手动停止&#xff0c;部署&#xff0c;这样用起来会很麻烦。所以&#xff0c;这篇文章就是自己通过.sh脚本一键启动&#xf…

工作压力测试

每个职场人都会遇到工作压力&#xff0c;在企业人力资源管理的角度来看&#xff0c;没有工作压力是人力资源的低效&#xff0c;适当的工作压力可以促使员工不断进取&#xff0c;然而每个人的抗压能力是不同的&#xff0c;同样的工作量和工作难度&#xff0c;不同的人在面对相同…

实战之-Redis代替session实现用户登录

一、设计key的结构 首先我们要思考一下利用redis来存储数据&#xff0c;那么到底使用哪种结构呢&#xff1f;由于存入的数据比较简单&#xff0c;我们可以考虑使用String&#xff0c;或者是使用哈希&#xff0c;如下图&#xff0c;如果使用String&#xff0c;注意他的value&…

快速入门java网络编程基础------Nio

一. NIO 基础 哔哩哔哩黑马程序员 netty实战视频 0.什么是nio&#xff1f; NIO&#xff08;New I/O&#xff09;是Java中提供的一种基于通道和缓冲区的I/O&#xff08;Input/Output&#xff09;模型。它是相对于传统的IO&#xff08;InputStream和OutputStream&#xff09;模型…

基于YOLOv7算法的高精度实时六类水果目标检测识别系统(PyTorch+Pyside6+YOLOv7)

摘要&#xff1a;基于YOLOv7算法的高精度实时六类水果目标检测系统可用于日常生活中检测与定位苹果&#xff08;apple&#xff09;、香蕉&#xff08;banan&#xff09;、葡萄&#xff08;grape&#xff09;、橘子&#xff08;orange&#xff09;、菠萝&#xff08;pineapple&a…

ElasticSearch(1):Elastic Stack简介

1 简介 ELK是一个免费开源的日志分析架构技术栈总称&#xff0c;官网https://www.elastic.co/cn。包含三大基础组件&#xff0c;分别是Elasticsearch、Logstash、Kibana。但实际上ELK不仅仅适用于日志分析&#xff0c;它还可以支持其它任何数据搜索、分析和收集的场景&#xf…

HarmonyOS 通过 animateTo讲解尺寸动画效果

上文 HarmonyOS讲解并演示 animateTo 动画效果 我们已经做出了基本的动画效果 也对 animateTo 的使用比较熟悉了 第一个参数是 配置动画参数的json 第二个参数 则是改变我们元素属性值的事件 但属性值 远远不止位置属性 本文 我们来说 通过尺寸变化 完成动画效果 如果你有看过…

[ACM题目练习] 前后手

题目1 A为了让数字总和最大&#xff0c;但是B想让数字总和最小。 题解 因为A先操作B后操作&#xff0c;所以B的策略一定是把当前剩下的数字中前1到 x 大的元素给乘上-1&#xff0c;那么A的策略是怎样的(通常这种题A没有策略&#xff0c;都是遍历所有的情况) (再接着优化&#…