【C语言进阶篇】C语言内存函数

news2024/11/16 13:36:41

目录

1.memcpy函数及其模拟实现

    1.1 memcpy函数的使用

    1.2 memcpy函数的模拟实现

2.memmove函数及其模拟实现

    2.1 memmove函数的使用

    2.2 memmove函数的模拟实现

3.memset函数

4.memcmp函数


1.memcpy函数及其模拟实现
    1.1 memcpy函数的使用

  memcpy函数是用来拷贝内存的函数,其拷贝的方式为一个字节一个字节地拷贝,使用时需要包含 <string.h> 的头文件。上面的 size_t num 参数的作用就是接收我们输入的想要拷贝的字节数;destination的意思是目的地,void* destination 参数是拷贝存放的目的地;source是起源的意思,const void* source就是我们所要拷贝的数据的起始地。两个接收地址的变量用 void* 类型来定义,说明可以接收不同类型的地址。来看看具体使用:

  这里我们将 arr 数组里20个字节的内存拷贝到 arr1 数组中去,因为arr数组的类型为int型,每个元素的大小为4个字节,因此,拷贝20个字节的内容相当于拷贝了 arr 中5个元素进 arr1 中。

  需要注意的是,如果 arr1 数组中本身有元素,在拷贝的时候,拷贝过去的元素会覆盖掉 arr1 中原有的元素:

  操作字符串:

    1.2 memcpy函数的模拟实现

  下面我们来自己动手模拟实现一下memcpy函数:

//模拟实现memcpy函数
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	//这一步是为了保存dest最开始指向的地址,因为在后续循环里的操作里,
	//dest的地址已经跑到后面去了,不指向最开始的地址

	while (num--)//这里是总共循环的次数,需要拷贝多少个字节就循环多少次
	{
		*(char*)dest = *(char*)src;

		// 因为dest与src都是void*类型,没法直接解引用操作,又因为
		// memcpy函数是一个字节一个字节拷贝的,因此我们在这进行拷贝操作的时候
		//将dest与src都强制类型转换为char*类型,这样就可以达到一个字节一个字节拷贝的目的了

		dest = (char*)dest + 1;
		src = (char*)src + 1;

		//这里的操作是在拷贝完一个字节以后,dest与src的地址都+1
		//去找下一个字节地址,进行下一个字节的拷贝

	}
	return ret;
}


int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr1[10] = { 0 };
	my_memcpy(arr1, arr, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}
2.memmove函数及其模拟实现
    2.1 memmove函数的使用

  memmove函数的作用和memcpy函数几乎一样,使用时也需要包含<string.h>的头文件但是memcpy函数相比于memmove函数来讲有一个缺陷,下面来看一段代码:

这里我们想要将 arr 数组中 1,2,3,4,5 的数据拷贝到 arr 数组中 3,4,5,6,7 的位置,那么结果按理来说应该最后输出打印的是 1,2,1,2,3,4,5,8,9,10。但实际上跟我们想的一样吗?

没错,想的和我们就是一样的,可能这个时候就有人想说我在水字数了。哎!此言差矣。虽然在这里最后输出的结果和我们设想的是一样的,但是,这仅是在VS编译器环境下一样而已,换做别的编译器,可能最后输出的就是下列结果:

为什么可能输出这个结果呢?我们来画图理解:

那么我们如果想要在一个数组里实现我们想要的效果,该怎么办呢?

  这就需要用到memmove函数了,memmove函数就可以专门用来处理这种拷贝内存与存放内存有重叠的情况:

在VS编译器中,memcpy函数也能够完成memmove的作用,我们不妨大胆猜测,在VS中memcpy函数的实现是和memmove函数一样的。

    2.2 memmove函数的模拟实现

  下面我们来手动模拟实现一下memmove函数。

  在模拟实现memmove函数之前,我们需要搞清楚memmove函数的原理,memmove函数的模拟实现相较于memcpy函数更为复杂,我们来逐个讨论:
 

  如何避免在拷贝内存的时候将后面需要拷贝的内存给改变呢?就像上面所说的改为 1,2,1,2,1,2,1,8,9,10 的情况。这时候我们可以想到,将内存从后往前拷贝不就行了,就像这样:

  ② 

  用从前往后的方法就可以达到我们上面的目的,但是,这里又会出现一个新的问题,如果我需要拷贝的内存在存放的内存后面呢,就像下面这种情况:

  这种情况从前往后存还适用吗?我们画图来探究一下:
  

  显然,这个时候再采用从后往前的方法就不行了。既然从前往后不行,那我们从前往后可行吗?画图探究下:

可以发现,这种情况从前往后拷贝存储是可行的,那么除了这两种情况还有别的情况吗?没有了,需要拷贝的内存拷贝内存目的地重复的情况一共就三种:  ① 需要拷贝的内存拷贝内存目的地前并且两者有重复部分   ② 需要拷贝的内存拷贝内存目的地后并且两者有重复部分  ③ 两者完全重复 。而第三种情况不管怎么拷贝都能达到我们的目的,因此,在模拟实现memmove函数时,我们只需要考虑前两种情况即可。下面来看代码:

  

void* my_memmove(void* dest, const void* src, size_t num)
{
		if (dest > src)//拷贝内存在拷贝存放的内存的前面,并且有内存叠加
		{
			dest = (char*)dest + num - 1;//将dest和src分别指向各自的最后一个字节
			src = (char*)src + num - 1;
			while (num--)//将内容一个字节一个字节进行拷贝
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest - 1;
				src = (char*)src - 1;
			}
		}
		else
		{
			while (num--)//拷贝内存在拷贝存放的内存的后面,并且有内存叠加
			{
				*(char*)dest = *(char*)src;//直接从第一个字节拷贝
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		}
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr+2, arr, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
3.memset函数

  memset函数顾名思义就是用来设置内存,将需要设置的内存中的值按照字节来设置成想要的内容,使用时也需要包含<string.h>的头文件

  

这里我们用memset函数将字符串s的前三个字节的内容改为了字符 ' x '。 

4.memcmp函数

memcmp函数是用来比较内存的,用法与strcmp函数十分相似,不同的是,strcmp函数是专门用来比较字符串的函数,而memcmp函数则可以用来比较各种类型。

这里的意思就是:当所比较的两个字符串,前一个大于后一个则返回一个大于0的数,反之则返回小于0的数,相等则返回0

这里我们调用的memcmp的时候,将 与 s1 进行比较,如果 s1 ,则返回一个大于0的数,反之,则返回小于0的数,如果相等,则返回0。这里 的第一个字符是a,而 s1 的第一个字符是b,在第一个字符就判断出了大小,后面也不用判断了,直接返回了小于0的数。

                                                       创作不易,点个赞再走呗,求求啦~😊

               

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

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

相关文章

【力扣精选算法100道】——带你了解(数组模拟栈)算法

目录 &#x1f4bb;比较含退格的字符串 &#x1f388;了解题意 &#x1f388;分析题意 &#x1f6a9;栈 &#x1f6a9;数组模拟栈 &#x1f388;实现代码 844. 比较含退格的字符串 - 力扣&#xff08;LeetCode&#xff09; &#x1f4bb;比较含退格的字符串 &#x1f3…

代码学习记录22--回溯算法第三天

随想录日记part22 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.17 主要内容&#xff1a;今天主要是结合类型的题目加深对回溯算法的理解&#xff1a;1.组合总和;2.组合总和 ;3.分割回文串。 39. 组合总和 40.组合总和II131.分割回文串 Topic1组合总和 题…

Error response from daemon Get server gave HTTP response to HTTPS client

使用docker compose拉起docker镜像时&#xff0c;若出现如下报错 Error response from daemon: Get "https://devops.test.cn:5000/v2/": http: server gave HTTP response to HTTPS client表示Docker守护进程无法从指定url获取响应&#xff0c; 可能原因有以下&…

深入理解jsp技术

目录&#xff1a; JSPJSP介绍JSP运行原理JSP标签的使用JSP原始标签的使用JSP的指令标签JSP的内置对象请求转发请求转发案例JSP中的四大作用域对象JSTL标签库EL表达式JSTL标签库与EL表达式的使用 JSP JSP介绍 JSP(全称Java Server Pages)Java服务端页面技术&#xff0c;是JavaEE…

结构体中的内存对齐是什么?一起搞懂它

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

LeetCode刷题记录:(11)组合(初识回溯算法)

leetcode传送通道 暂时记录&#xff0c;这篇没啥营养&#xff0c;不用看了 class Solution {List<List<Integer>> result new ArrayList<>(); // 存所有组合List<Integer> path new LinkedList<>(); //存每一个组合public List<List<Int…

聚信产服:引领多元化产业服务迈向新纪元

聚信产服成立于2014年&#xff0c;一家在产业服务领域拥有深厚积淀的综合性服务机构&#xff0c;专注于政府招商、园区运营、企业选址咨询及产业链服务等多元化业务。我们致力于通过高效、专业的服务&#xff0c;为政府、企业及投资者提供全方位的产业支持&#xff0c;推动区域…

【鸿蒙HarmonyOS开发笔记】动画过渡效果之布局更新动画

概述 动画的原理是在一个时间段内&#xff0c;多次改变UI外观&#xff0c;由于人眼会产生视觉暂留&#xff0c;所以最终看到的就是一个“连续”的动画。UI的一次改变称为一个动画帧&#xff0c;对应一次屏幕刷新&#xff0c;而决定动画流畅度的一个重要指标就是帧率FPS&#x…

计算机二级(Python)真题讲解每日一题:《十字叉》

描述‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬ ‪‬‪‬‪‬‪‬‪‬‮‬‪…

React Hooks全部总结

Hooks 概念理解 学习目标&#xff1a; 理解 Hooks 的概念及解决的问题 什么是 hooks hooks 的本质&#xff1a; 一套能够使函数组件更强大、更灵活的&#xff08;钩子&#xff09; React 体系里组件分为类组件和函数组件 多年使用发现&#xff0c;函数组件是一个更加匹配 Rea…

解决:visio导出公式为pdf图片乱码问题

今天需要将Visio编辑好的以后的图输出pdf&#xff0c;但是点击保存后公式部分一直乱码&#xff0c;如下图所示 保存为pdf后会变成&#xff1a; 解决方案&#xff1a;保存时点击文件下方的快速打印&#xff0c;存到桌面&#xff0c;不要直接点击保存

Vue3学习日记 Day1

一、简介 1、简介 Vue3是新的默认版本&#xff0c;拥有更快的速度&#xff0c;更好的语法 二、使用create-vue搭建Vue3项目 1、创建项目 1、介绍 create-vue是Vue官方新的脚手架工具&#xff0c;底层切换为了vite&#xff0c;为开发提供极速响应 2、使用 2.1、确定环境条件 2…

关于 闰年 的小知识,为什么这样判断闰年

闰年的规定&#xff1a; 知道了由来&#xff0c;我们就可以写程序来判断&#xff1a; #include <stdio.h> int main() {int year, leap;scanf("%d",&year);if((year%4 0 && year%100 ! 0) || year%400 0)leap 1;else leap 0;if(leap) printf(…

Python大数据实践:selenium爬取京东评论数据

准备工作 selenium安装 Selenium是广泛使用的模拟浏览器运行的库&#xff0c;用于Web应用程序测试。 Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样&#xff0c;并且支持大多数现代 Web 浏览器。 #终端pip安装 pip install selenium #清华镜像安装 p…

创新应用2:nnmf+DBO+K-Medoids聚类,蜣螂优化算法DBO优化K-Medoids,适合学习和发paper。

创新应用2&#xff1a;nnmfDBOK-Medoids聚类&#xff0c;蜣螂优化算法DBO优化K-Medoids&#xff0c;适合学习和发paper。 一、蜣螂优化算法 摘要&#xff1a;受蜣螂滚球、跳舞、觅食、偷窃和繁殖等行为的启发&#xff0c;提出了一种新的基于种群的优化算法(Dung Beetle Optim…

断言assert是什么?

assert是什么&#xff1f; assert断言&#xff0c;是一个被定义在<assert.h>头文件中的一个宏&#xff0c;而不是一个函数。 可以用来检查数据的合法性&#xff0c;但是频繁的调用极大影响了程序的性能&#xff0c;增加了额外的开销。可以通过#define NDEBUG来禁用asse…

计算机考研|408还是自命题?看这一篇就够了

计算机考研自命题都担心考不上&#xff0c;408估计更能让你头秃 不要光看着408的分数线低&#xff0c;自命题分数线高&#xff0c;408想拿到高分是很困难的...&#x1f630; 某双飞一本 身边大部分人都去考数据结构是因为考试内容相比于408来说少了很多&#xff0c;时间更好分配…

MySQL基础-----多表查询之子查询

目录 前言 子查询概述 1.概念 2.分类 一、标量子查询 二、列子查询 三、行子查询 四、表子查询 前言 上一期我们讲了内外连接查询以及自连接查询&#xff0c;那么本期我们就学习多表查询的子查询。本期会详细讲解什么是子查询&#xff0c;以及子查询的相关功能&#xf…

Java项目:63 ssm网上花店设计+vue

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统具备友好性且功能完善。管理员登录进入后台之后&#xff0c;主要完成花材选择管理&#xff0c;用户管理&#xff0c;鲜花管理&#xff0c;鲜花出入…

18 优先级队列

priority_queue介绍 1.优先级队列是一种容器适配器&#xff0c;根据弱排序标准&#xff0c;它的第一个元素总是最大的 2.此上下文类似于堆&#xff0c;堆中可以随时插入元素&#xff0c;检索最大堆元素 3.优先队列实现为容器适配器&#xff0c;容器适配器即将特定容器类封装作…