C语言-内存操作函数

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*类型的数据。


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

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

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

相关文章

Java的数组定义和使用

目录 1.前言 2.数组的概念 3.在Java中的创建和初始化 3.1数组的创建 3.2数组的初始化 4.关于使用 4.1数组元素的访问 4.2数组的遍历 4.3length和length()的区别 5.数组其实是引用类型数据 5.1初始JVM的内存分布 5.2基本类型变量与引用类型变量的区别 5.3关于null的认识 5.4设计…

ssm057学生公寓管理中心系统的设计与实现+jsp

学生公寓管理中心系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生公寓管理中心系统就是在这样的大环境下诞生&#xff0c;其可以帮助管…

Matlab对多个输入信号进行数值排序提取特定值

1、将多个信号转为一个数组信号输出&#xff0c;在这里需要注意&#xff0c;数据类型是否统一&#xff1b; 2、使用Sort模块&#xff0c;进行排序&#xff08;可设置排序方向&#xff09;&#xff0c;得到排序后的新数组以及对应的索引号&#xff1b; 3、设置想要的索引号&…

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

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

文献学习-38-用于增量组织病理学分类的内存高效提示调整

​ Memory-Efficient Prompt Tuning for Incremental Histopathology Classification Authors: Yu Zhu, Kang Li, Lequan Yu, Pheng-Ann Heng Source: The Thirty-Eighth AAAI Conference on Artificial Intelligence (AAAI-24) ​​ Abstract 最近的研究在组织病理学分类方面…

杨元庆:人工智能需要更加私密化和个性化

4月18日&#xff0c; 2024联想创新科技大会Tech World在上海举办。联想集团董事长兼CEO杨元庆在演讲中表示“我们的愿景&#xff0c;就是让人工智能走下云端&#xff0c;真正落地&#xff0c;走进千家万户、千行百业”。 这意味着&#xff0c;我们需要让人工智能更加私密化和个…

低代码开发平台:创新工具,颠覆传统

低代码开发平台是近年来迅速崛起的一种创新型软件开发工具&#xff0c;以其高效、灵活的开发模式正颠覆着传统的开发方式。不再需要编写大量繁杂的代码&#xff0c;开发者们可以在图形化界面中以拖拽、配置的方式进行应用的搭建&#xff0c;大大提高开发效率和质量。本文将全面…

element-plus关于el-radio-group选择一个单选按钮,全被选中

问题 使用el-radio-group 组件&#xff0c;进行多个互斥选择时&#xff0c;点击一个选项时&#xff0c;全部选择。设置radio的默认值也无法选中 代码为官方实例 <template><el-radio-group v-model"radio"><el-radio :value"3">Option…

Games101-光线追踪(辐射度量学、渲染方程与全局光照)

Basic radiometry (辐射度量学) 光的强度假定l为10&#xff0c;但是10是什么。 Whitted-Style中间了很多不同简化&#xff0c;如能看到高光&#xff0c;表示做了布林冯着色&#xff0c;意味着一个光线打进来后会被反射到一定的区域里&#xff0c;而不是沿着完美的镜像方向&…

从三大层次学习企业架构框架TOGAF

目录 前言 掌握TOGAF的三个层次 层次1&#xff1a;怎么学&#xff1f; 层次2&#xff1a;怎么用&#xff1f; 层次3&#xff1a;怎么思&#xff1f; 结束语 前言 对于一名架构师来讲&#xff0c;如果说编程语言是知识库层次中的入门石&#xff0c;那么企业架构框架则相当…

【微信小程序从入门到精通(项目实战)】——微电影小程序

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

Modelsim自动化仿真脚本(TCL)——简单实例

目录 1. Modelsim与TCL脚本的关系 2.实验文件 2.1设计文件 2.2仿真测试文件 2.3. 脚本文件 3. 实验步骤 3.1. 创建文件夹 3.2. 指定路径 3.3. 创建工程 3.4. 运行命令 3.4. 实验效果 1. Modelsim与TCL脚本的关系 TCL&#xff08;Tool Command Language&#xff09;是…

吴恩达深度学习笔记:深度学习的 实践层面 (Practical aspects of Deep Learning)1.4-1.5

目录 第一门课&#xff1a;第二门课 改善深层神经网络&#xff1a;超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第一周&#xff1a;深度学习的 实践层面 (Practical aspects of Deep Learning)…

[C++][算法基础]分解质因数(试除法)

给定 n 个正整数 ai&#xff0c;将每个数分解质因数&#xff0c;并按照质因数从小到大的顺序输出每个质因数的底数和指数。 输入格式 第一行包含整数 n。 接下来 n 行&#xff0c;每行包含一个正整数 ai。 输出格式 对于每个正整数 ai&#xff0c;按照从小到大的顺序输出其…

同城O2O跑腿系统源码:外卖配送APP的技术架构与设计思路解析

今天&#xff0c;我将为大家详解同城O2O跑腿系统源码&#xff0c;剖析外卖配送APP的技术架构与设计思路。 一、用户端与商家端 用户端提供用户注册、登录、浏览菜单、下单、支付等功能&#xff0c;而商家端则负责商家入驻、菜单管理、订单处理等。这两个端的技术架构相对独立…

在Linux操作系统中文件目录特殊权限

管理员用户和普通用户都可以使用passwd命令来给用户设置密码 用户密码保存在/etc/shadow suid 4 sgid 2 sticky bit 1 suid&#xff08;只要是针对可执行文件进行设置。&#xff09; {当没有操作要求时&#xff0c;不可以擅自对shell&#xff0c;python脚本加上suid权…

java动态代理--cglib代理

1.概述 CGLIB动态代理是针对类实现代理&#xff08;无需实现接口&#xff09;&#xff0c;为了弥补jdk的不足&#xff0c; Cglib 不基于接口&#xff0c;是基于父子的继承关系&#xff08;被代理的对象是代理对象的父类&#xff09;&#xff0c;通过重写的形式扩展方法 2.定…

带头循环双向链表专题

1. 双向链表的结构 带头链表⾥的头节点&#xff0c;实际为“哨兵位”&#xff0c;哨兵位节点不存储任何有效元素&#xff0c;只是站在这⾥“放哨 的” “哨兵位”存在的意义&#xff1a; 遍历循环链表避免死循环。 2. 双向链表的实现 2.1双向链表结构 typedef int DataTyp…

【光伏科普】光伏软件都有些什么功能?

光伏软件是为满足光伏行业特定需求而设计的工具&#xff0c;它涵盖了多个方面的功能&#xff0c;以支持光伏系统的设计、分析、优化和运营。以下是光伏软件通常具备的一些核心功能&#xff1a; 1.光伏系统设计 组件布局优化&#xff1a;根据地理位置、光照条件、阴影遮挡等因素…

开源在线表单工具 HeyForm 使用教程

HeyForm 是一个非常出色的开源在线表单工具&#xff0c;可以通过直观的拖拽式编辑器&#xff0c;快速构建出美观实用的表单。 HeyForm 的功能非常丰富&#xff1a; 支持丰富的输入类型&#xff0c;从基础的文本、数字到高级的图片选择、日期选择、文件上传等&#xff0c;一应俱…