C语言-内存操作函数

news2024/11/25 12:53:11

C语言有一类内存函数,他们可以以字节为单位进行数据的拷贝、追加,甚至可以替代部分字符串函数。于是让我们来狠狠地学习它一百万遍吧~

1.memcpy函数的使用和模拟实现

void * memcpy ( void * destination, const void * source, size_t num );

1.1memcpy函数的使用 

1.memcpy函数从source的位置开始向后赋值num个字节的数据到destination指向的内存位置。由于是以字节为单位进行拷贝,所以无关数据类型。

2.memcpy函数在遇到'\0'时并不会停下来。

3.我们不希望source和destination有重叠的部分。如果它们有任何重叠的部分,复制的结果都是未定义的。 

#include <stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	//0 1 2 3 4
	int arr2[20] = { 0 };
	//0 1 2 3 4

	memcpy(arr2, arr1, 20);

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", arr2[i]);//1 2 3 4 5 0 0 0 0 0...
	}

	return 0;
}

1.2memcpy函数的模拟实现

由于char类型的数据为1个字节,所以我们可以将数据类型华为char再赋值,直到num个为止。

void my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//((char*)dest)++;
		src = (char*)src + 1;//((char*)src)++;
	}
	return ret;
}

我们从我们自己模拟实现的代码来看,为什么说不能有重复的地方呢?我们来看下面的图解就明白了

 假设我们有一数组,里面元素为1 2 3 4 5 6 7 8 9 10,现在我们想将2 3 4 5这20个字节的数据赋值到4 5 6 7中。那么我们希望得到的应该是1 2 3 2 3 4 5 8 9 10,而按照我们代码的逻辑,原本的4和5的位置已经被2和3覆盖了,所以赋值给原本6和7的位置就是2和3而不是4和5了,这样我们得到的结果就是1 2 3 2 3 2 3 8 9 10,不符合我们的预期。

虽然说在VS中memcpy有时候确实可以在有重叠的情况下赋值,但是标准中那么说,我们还是不要这样使用为好,万一有问题呢?所以memcpy还是只适用于dest和src无重叠部分的情况。那有重叠的情况该怎么办呢?我们接下来马上介绍。

2.memmove函数的使用和模拟实现

 void * memmove ( void * destination, const void * source, size_t num );

2.1memmove函数的使用

1.memmove函数与memcpy函数的区别就是memmove函数处理的源内存块和目标内存块可以有重叠部分。

2.如果源空间和目标空间出现重叠,我们就是用memmove函数来处理。

#include <stdio.h>
#include <assert.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	memmove(arr + 2, arr, 5 * sizeof(int));

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//1 2 1 2 3 4 5 8 9 10
	}

	return 0;
}

2.2memmove函数的模拟实现

memmove的实现要比memcpy要复杂,因为要考虑到区域块重叠的情况,所以我们也要分情况讨论。

第一种情况,也就是上面memcpy函数举例的情况

我们能看到,下面左边的就是按照我们memcpy模拟实现的代码的逻辑,这显然是不行的。但是我们发现,左边的逻辑是将src的内容从左边开始向dest复制,那如果我们从右边开始,即先把src最后一个元素先给到dest最后一个元素,然后依次往前呢?显然是可以的,这样就不会导致4和5还没赋值过去就被2和3覆盖,结果显然就是正确的。所以这种情况我们可以用从后往前赋值的逻辑处理

第二种情况,我们将src和dest换个位置,希望将4 5 6 7赋值到2 3 4 5的内存块中,那我们期望的结果应该是1 4 5 6 7 6 7 8 9 10。

 我们也向上次那样分析,可以发现从前往后传值是正确的,而从后往前传就会导致重叠部分还没赋值它原本的值就已经被覆盖了,所以这种情况我们可以用从前往后赋值的逻辑处理

 现在理解了上面两种情况,我们来尝试总结一下

 

当目标内存块dest在源内存块src左边或在src左边且与src有重叠时(dest在D1区域或D1+D2区域),我们采用从前向后赋值的方法;而当目标内存块dest在源内存块src右边或在src右边且与src有重叠时(dest在D3区域或D3+D2区域),我们采用从后向前赋值的方法。

那我们现在就可以开始写代码了。

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		//前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后->前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

其实只要好好画图分析,分类讨论,其实也不难。

3.memset函数的使用

void * memset ( void * ptr, int value, size_t num );

1.memset函数是用来设置内存的,可以将内存中的值以字节为单位设置成想要的内容。

2.ptr是指向被填充的内存块,value是要把原数据修改成什么,num是你要设置多大的字节的内存。

#include <stdio.h>
#include <assert.h>

int main()
{
	char arr[] = "hello world";
	memset(arr + 2, 'y', 7);
	printf("%s\n", arr);//heyyyyyyyld

	return 0;
}

特别注意当我们向修改整型数组的值的时候,出了0之外的值我们是没办法设置的 

#include <stdio.h>

int main()
{
	int arr[5] = { 0 };
	memset(arr, 1, 5*sizeof(int));
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

向上面这样想把数组中的每个值都修改为1是做不到的,我们来看看代码运行的结果 :

其原因是,memset是以字节为单位来设置的,我们来看看数组元素的内存:

如我们所见,memset将我们每个字节都设置成了1,那么一个整型为4个字节,即每个字节为01 01 01 01,换算成十进制这是个很大的数字,所以我们的结果才会都为16843009。

4.memcmp函数的使用

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

1.比较从ptr1和ptr2指针指向的位置开始,向后的num个字节的内容 

#include <stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[] = { 1,2,3,4,8,8,8 };

	printf("%d\n", memcmp(arr1, arr2, 16));//0
	printf("%d\n", memcmp(arr1, arr2, 17));//-1

	return 0;
}

 其实和strncmp函数是比较相似的,只不过memcmp函数是以字节为单位的,可以比较不限于char*类型的数据。


那么到这里内存函数相关的内容就结束了,其实还是很简单的吧~

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

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

相关文章

单调队列(C/C++)

引言&#xff1a; 单调队列和单调栈都是一种数据结构&#xff0c;应用十分广泛&#xff0c;在蓝桥杯、ICPC、CCPC等著名编程赛事都是重点的算法&#xff0c;今天博主将自己对单调栈与单调队列的理解以及刷题的经验&#xff0c;用一篇博客分享给大家&#xff0c;希望对大家有所…

springboot中mongodb连接池配置-源码分析

yml下spring.data.mongodb 以前mysql等在spring.xxx下配置&#xff0c;现在springboot新版本&#xff08;小编3.2.3&#xff09;在spring.data.xxx下了&#xff0c;如下所示&#xff0c;mongodb的配置在spring.data.mongodb下&#xff1a; 连接池相关参数配置-源码分析 拼接在…

使用CCS软件查看PID曲线

在刚开始学习PID的时候&#xff0c;都需要借助PID的曲线来理解比例&#xff0c;积分&#xff0c;微分这三个参数的具体作用。但是这些曲线生成一般都需要借助上位机软件或者在网页上才能实现。如果是在单片机上调试程序的话&#xff0c;想要看曲线&#xff0c;一般就是通过串口…

C语言 【基础语法】

一、编程环境搭建 编译器&#xff1a;gcc 集成开发环境&#xff1a;vscode 1.1 安装vscode 1.2 设置中文包 插件 1.3 设置C/C扩展 安装 C/C Compile Run extension 和 C/C Extension Pack 扩展 二、基础语法 2.1 第一个c语言程序 2.2 数据类型 2.2.1 变量的语法(重点) …

氯氟氰虫酰胺杀灭活性好成本低 目前全球市场规模小

氯氟氰虫酰胺杀灭活性好成本低 目前全球市场规模小 氯氟氰虫酰胺&#xff0c;通用名为cyhalodiamide&#xff0c;是一种双酰胺类杀虫剂&#xff0c;外观为白色固体粉末状。 氯氟氰虫酰胺属于鱼尼丁受体作用剂&#xff0c;为邻苯二甲酰胺结构的双酰胺类杀虫剂&#xff0c;作用机…

Vision Transformer (ViT)浅析

Vision Transformer (ViT) 概述 为了将Transformer引入视觉任务&#xff0c;Google团队开发出了Vision Transformer (ViT)&#xff0c;其中ViT模型以及变种在图像分类任务上一骑绝尘 ViT的结构 ViT首先将图像( R H W C \mathbb{R}^{H\times W\times C} RHWC)划分为多个Patc…

一周学会Django5 Python Web开发-Django5模型数据新增

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计46条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

上位机图像处理和嵌入式模块部署(树莓派4b实现动态插件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 和上位机一样&#xff0c;我们的智能硬件如果想应用到更多的场景&#xff0c;那么势必需要实现更多的算法。这些算法和算法之间最好是松散耦合的插…

思维导图软件Xmind for Mac 中文激活版 支持M

XMind是一款非常受欢迎的思维导图软件&#xff0c;它应用了Eclipse RCP软件架构&#xff0c;注重易用性、高效性和稳定性&#xff0c;致力于帮助用户提高生产率。 Xmind for Mac 中文激活版下载 XMind的程序主体由一组插件构成&#xff0c;包括一个核心主程序插件、一组Eclipse…

公众号开发:获取域名

公众号中的基本配置要求需要连接服务器地址&#xff0c;这个服务器地址必须是域名&#xff0c;但是域名需要进行购买&#xff0c;因此可以使用到一个内网穿透工具&#xff1a;ngrok 进行测试 国内的就可以了Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器 首先花两块钱实名…

Mybatis之SqlNodeSqlSource

SqlNode SqlNode接口 apply()是SqlNode 接口中定义的唯一方法&#xff0c;该方法会根据用户传入的实参&#xff0c; 参数解析该SqlNode所记录的动态SQL节点&#xff0c;并调用DynamicContext.appendSql()方法将解析后的SQL片段追加到DynamicContext.sqlBuilder中保存。当SQL节…

[Collection与数据结构] 二叉树(二):二叉树精选OJ例题(上)

1. 判断是否为相同的二叉树 OJ链接 public boolean isSameTree(Node p, Node q) {if (p null && q ! null || p ! null && q null){//结构不同return false;}if (p null && q null){//结构相同,都是空树return true;}if (p.value ! q.value){//…

机器人流量激增:恶意机器人活动升级与新型规避技术挑战企业安全防御

近日&#xff0c;根据Cyber News引用Thales Imperva Bad Bot发布的最新研究报告&#xff0c;揭示了一个令人警醒的现象&#xff1a;2023年&#xff0c;互联网总流量中的49.6%由机器人贡献&#xff0c;相较于上一年增长了2%&#xff0c;创下了自2013年监测以来的历史新高。这一显…

希亦、固特、大宇超声波清洗机值不值得买?宝藏机型测评争霸

在当代生活的快节奏中&#xff0c;眼镜成了我们不可或缺的伙伴&#xff0c;无论是助力视力的提升还是时尚造型的加分项。然而&#xff0c;许多人忽视了眼镜清洁的重要性&#xff0c;不知道不清洗眼镜会带来诸多危害&#xff0c;从而影响健康和生活质量。长时间累积的污垢、油脂…

苹果开发初学者指南:Xcode 如何为运行的 App 添加环境变量(Environmental Variable)

概览 Xcode 15 在运行 SwiftUI 代码时突然报告如下警告&#xff1a; Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem. 不仅如此…

【智能算法】花朵授粉算法(FPA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2012年&#xff0c;Yang等人受到自然界花朵授粉过程启发&#xff0c;提出了鸭群算法&#xff08;Flower Pollination Algorithm, FPA&#xff09;。 2.算法原理 2.1算法思想 FPA基于自然界花朵授…

idea项目启动异常:Command line is too long.

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; idea中启动项目报错&#xff1a; 解决方案 在idea 的运行配置中&#xff0c;修改enviroment下的shorten command line 为jar manifest 注&#xff1a; 有时shorten command line 可能不是默认存在的…

Kafka复习

消息中间件的作用: 异步处理: 与并行相比,虽然减少了时间,但是还是得等待其他线程执行完,但是消息中间件对于简单的业务处理,还要引入一个中间件也比较复杂如果我投递了简历之后需要发送成功邮件以及短信,就可以交给消息中间件就像数据库、redis数据一致性,需要用到延迟…

揭秘网上问卷调查:多样化类型助力数据收集

在当今数字化时代&#xff0c;问卷调查作为一种便捷、高效的数据收集和分析方式&#xff0c;被广泛运用于市场调研、学术研究、消费者反馈等领域。而在网上进行问卷调查更是提高了调查效率和覆盖范围&#xff0c;因此&#xff0c;了解网上的问卷调查类型对于设计合适的调查问卷…

如何使用Postgres的JSONB数据类型进行高效查询

文章目录 解决方案1. 创建包含JSONB列的表2. 插入JSON数据3. 使用GIN索引加速查询4. 执行高效的JSONB查询 示例代码解释 PostgreSQL的JSONB数据类型提供了一种灵活的方式来存储和查询JSON格式的数据。JSONB不仅允许你在PostgreSQL数据库中存储JSON文档&#xff0c;而且还对这些…