C语言常用的内存函数

news2025/1/11 5:44:39

在上一篇博客中我为大家分享了一些常用的字符串函数,以及它们的用法和模拟实现。通过字符串函数中的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;
}

那么关于比较常用的内存函数的知识,就给大家分享到这里啦,如果有讲解的有问题或者不清楚的地方,还希望各位在评论区多多指出,我也会吸取教训,多多学习的,那么我们下期再见啦~

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

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

相关文章

大模型混合云,到了系统性创新突围的关键时刻

文 | 智能相对论 作者 | 陈泊丞 亚马逊与Anthropic、微软与OpenAI以及华为云提出的大模型混合云概念等等种种信号都在表明&#xff0c;云计算与大模型深度融合发展的趋势已是业内共识。 目前&#xff0c;以生成式AI为导向&#xff0c;越来越多的公司开始加速大模型技术在云计…

够豪横,310万不要了,后续损失恐上千万

近日&#xff0c;深圳一套法拍房引起了整个圈子的轰动&#xff01; 20年前花费382万购入的豪宅&#xff0c;现在竟能拍到8389万&#xff0c;翻了22倍&#xff01; 就在大家都感慨深圳顶豪的购买力惊人时&#xff0c;事情却迎来了更大的反转&#xff01; 该豪宅竟重现法拍市场…

【HarmonyOS】鸿蒙应用实现调用系统地图导航或路径规划

【HarmonyOS】鸿蒙应用实现调用系统地图导航或路径规划 前言 在涉及地图业务中&#xff0c;调用地图导航和路径规划是三方应用中较为常见的功能。 若只是子业务需要地图导航效果&#xff0c;整个APP内部集成地图去实现导航或者路径规划&#xff0c;会造成SDK集成冗余。毕竟很…

校园外卖服务系统设计与实现

TOC springboot292校园外卖服务系统设计与实现 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&…

若依 Ruoyi4.7.9 创建页面并显示

自行从https://gitee.com/y_project/RuoYi下载源代码&#xff0c;这个不再赘述。 // 下载后会发现现在的Ruoyi环境&#xff0c;分成了6个模块&#xff1a; 模块主要的介绍大家可以看文档。 官网地址&#xff1a;https://doc.ruoyi.vip/ruoyi/ 我下载的这个是基于mvc框架&…

CTFHUB-技能树-Web题-RCE(远程代码执行)-远程包含-命令注入-过滤空格

CTFHUB-技能树-Web题-RCE&#xff08;远程代码执行&#xff09;-远程包含-命令注入-过滤空格 就是过滤掉了空格 找能代替空格的代替就行了 使用IFS$9、%09、<、>、<>、{,}、%20、${IFS}、${IFS}来代替空格 127.0.0.1;cat${IFS}xxxx.php先来查看一下目录 127.0.0…

python可视化数据练习(地形图)

一、数据展示&#xff08;可通过目录直接跳转到代码部分&#xff09; {"lastUpdateTime":"2021-08-18 10:53:30","chinaTotal":{"confirm":122482,"heal":112984,"dead":5669,"nowConfirm":3829,"…

基于Spring boot的名城小区物业管理系统

TOC springboot240基于Spring boot的名城小区物业管理系统 绪论 1.1研究背景与意义 1.1.1研究背景 近年来&#xff0c;第三产业发展非常迅速&#xff0c;诸如计算机服务、旅游、娱乐、体育等服务行业&#xff0c;对整个社会的经济建设起到了极大地促进作用&#xff0c;这一…

Xilinx(AMD)的怪异行为——ila的radix没有real格式

使用vivado 2021.1的过程中发现&#xff0c;仿真时&#xff0c;可以选择把数据的radix按照定点或浮点格式显示&#xff1a; 显示效果如下&#xff1a; 仿真完后&#xff0c;在ila中观察&#xff0c;发现定点和浮点显示的菜单少了几项&#xff1a; xilinx为什么会把这么有用的…

剑指offer--面试题58.翻转字符串

题目描述 输入一个英文句子&#xff0c;翻转句子中单词的顺序&#xff0c;但单词内字符的顺序不变。为简单起见&#xff0c;标点符号和普通字母一样处理。例如输入字符串”I am a student.“,则输出”student. a am I“。 算法分析 完整代码 #include <string.h> //把起…

html+css+js网页设计 作业歌帝梵官网首页1个页面6个js效果

htmlcssjs网页设计 作业歌帝梵官网首页1个页面6个js效果 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获…

安卓主板_MTK联发科主板定制开发|PCBA定制开发

MTK联发科安卓主板&#xff0c;采用MT6762八核平台方案&#xff0c;支持谷歌Android 11.0系统&#xff0c;MT6762采用ARM八核A53内核芯片、主频高达2.0GHz&#xff0c;GPU采用ARM PowerVR GE8329650MHZ&#xff0c;支持主流19201080分辨率&#xff0c;支持硬解H.264&#xff0c…

Typora使用教程-修改配置文件-免费使用

Typora特点 简洁美观&#xff1a;Typora的界面非常简洁&#xff0c;没有繁琐的菜单和工具栏&#xff0c;只有一个干净的编辑窗口。它使用 Markdown语法&#xff0c;将文本转化为漂亮的排版&#xff0c;无需关注样式和格式的细节。所见即所得&#xff1a;Typora实时显示Markdow…

希尔排序,详细解析(附图解)

1.希尔排序思路 希尔排序是一种基于插入排序的算法&#xff0c;通过将原始数据分成若干个子序列&#xff0c;然后对子序列进行插入排序&#xff0c;逐渐减小子序列的间隔&#xff0c;最后对整个序列进行一次插入排序。 1.分组直接插入排序&#xff0c;目标接近有序--------…

「Unity3D」属性面板(Inspector)上增删操作,序列化的Dictionary对象的k-v数值

Unity无法序列化Dictionary对象&#xff0c;可以通过实现ISerializationCallbackReceiver接口&#xff0c;来自定义实现&#xff0c;即&#xff1a;搞一个key list和一个value list分别存储dict的k-v&#xff0c;于是属性面板上就会显示&#xff0c;k-v的两个列表。 但这会有一…

【鸿蒙基础系列】鸿蒙基础组件

&#x1f41f;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢编程&#x1fab4; &#x1f421;&#x1f419;个人主页&#x1f947;&#xff1a;Aic山鱼 &#x1f420;WeChat&#xff1a;z7010cyy &#x1f988;系列专栏&#xff1a;&#x1f3de;️ 前端-JS基础专栏✨前…

stm32智能颜色送餐小车(红外光管避障)

大家好啊&#xff0c;我是情谊&#xff0c;今天我们来介绍一下我最近设计的stm32产品&#xff0c;我们在今年七月份的时候参加了光电设计大赛&#xff0c;我们小队使用的就是stm32的智能送餐小车&#xff0c;虽然止步于省赛&#xff0c;但是还是一次成长的经验吧&#xff0c;那…

Sql与Rce注入相关漏洞复现

目录 sqli-labs注入第38&#xff0c;48关 第38关&#xff08;单引号闭合&#xff09; ​编辑 第48关 (GET请求-基于错误-盲注-数字型-order by 排序 ​编辑 贷齐乐系统多处Sql注入漏洞 环境搭建 将贷齐乐源码放入phpstudy中的www目录下 在phpstudy上创建网站&#xff1…

DevEcoStudio对Gitee进行变基与合并

当尝试将本地分支的更改推送到远程仓库&#xff0c;但是远程仓库中的该分支已经有了您本地分支中没有的提交时&#xff0c;会出现这个提示。 具体来说&#xff0c;这个提示意味着&#xff1a; 推送被拒绝&#xff1a;不能直接将更改推送到远程仓库&#xff0c;因为远程仓库中…

WeNet模型学习笔记

WeNet是一个端到端语音识别模型&#xff0c;由于其优越的特性&#xff0c;成为近几年语音识别领域的热门模型。WeNet模型是一种CTC和attention结构的混合体&#xff0c;以Conformer或Transformer作为encoder&#xff0c;并使用attention decoder对CTC的输出进行重新打分(rescor…