鹏哥C语言复习——内存函数

news2024/10/6 16:19:47

目录

一.memcpy函数

二.memmove函数

三.memset函数

四.memcmp函数


 

一.memcpy函数

该函数是针对内存块进行拷贝操作,mem即为memory,是内存的意思;cpy就是copy,是拷贝的意思

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };

如上两个数组,我们如何才能不通过循环的方式将arr数组中的前五个元素放入arr2数组呢?

这时就需要用到memcpy函数

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

注意事项:

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的位置
  • 这个函数在遇到'\0'时并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的(源空间和目标空间在内存使用上不能有重叠)
  • 使用时需要引用头文件<string.h>
  • 学会memcpy函数的模拟实现
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr, 20); //就是把20个字节的内容从arr数组中存入到arr2数组中,即5个元素(整型变量一个占4字节)
	return 0;
}

 模拟实现:

10df79bf13ea4054bde78f69ec6993bf.jpg

 两个指针在开始时,分别指向开始处,可以通过一个字节一个字节拷贝来实现memcpy的功能

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr, 20); //就是把20个字节的内容从arr数组中存入到arr2数组中,即5个元素(整型变量一个占4字节)
	return 0;
}

//memcpy函数拷贝结束后,会返回目标空间的起始地址,但返回以后需不需要创建一个新指针接收返回的起始地址是随意的
void* my_memcpy(void* dest,const void* src, unsigned int num) //把万能指针作为形参是为了解决实参的不同类型(可能是字符型也可能是整型)
{
	void* ret = dest;
	//一个字节一个字节拷贝,循环num次即可
	int i = 0;
	assert(dest && src); //两个指针不能为空
	while (num--) //后置--,先使用再--
	{

		*(char*)dest = *(char*)src; //为了一个字节一个字节拷贝,所以可以将两个万能指针强转成char*指针(指向char类型的指针),然后再解引用,访问1字节的数据
		src = (char*)src + 1;   //用于运算的表达式必须是指向完整对象类型的指针,因此需要再次进行强制转换;但用于接收的表达式可以是万能指针
		dest = (char*)dest + 1; //同时不能写成(char*)src++的形式,因为强制转换操作符的优先级与++相同,但这两个操作符是从右往左运算,先使用了src,再++,最后在强转,违背了上一条规律
								//但可以写成((char*)src)++这种形式,可以看成(char*)src = (char*)src + 1, = 前面的src强制转化就是在把void * 类型强制转换为char* 类型,依然满足需求
								//笔者在此推荐src = (char*)src + 1这种形式,因为这种形式下用于接收的src还是void*类型,什么指针类型都能接收,而char* 类型只能接受char* 类型
                                //而对于强制类型转换,一个变量的数据类型被强制转换后,出了这条代码后就不会再保持被强制转换后的数据类型了,因为强制类型转换只是作为运算符存在,并不能真的改变src或者dest的数据存放类型
	}
	return ret;
}

注:模拟实现的代码思想与注意事项在上文代码中的批注里

1.那为什么我们必须一个字节一个字节拷贝呢?

如果是以整型变量的数据大小(4字节)来拷贝,那如果我们传入的num大小为7就无法完成拷贝工作,因此需要以1个字节大小来进行拷贝

2.对于上文中所提到的source和destination有任何的重叠,复制的结果都是未定义的,是什么意思?

例如要让arr的前5个数据(12345)memcpy到第三个数据(3)开始中去,即使memcpy函数可以让arr数组变成1212345(vscode编译器),但是是存在复制结果未定义风险的

 

二.memmove函数

该函数与memcpy函数的差别就是memmove处理的源内存块和目标内存块可以是重叠的,因此如果源空间和目标空间出现重叠,就得使用memmove函数处理

模拟实现:

在模拟函数时,关键在于如何将一个数组里的重叠元素拷贝,共分为以下两种情况:

1.src(源空间)的首元素在dest(目标空间)的首元素前,这种情况下需要从dest以及src的末尾元素开始进行拷贝

2.src的首元素在dest的首元素后,这种情况下需要从dest以及src的首元素开始进行拷贝

情形1: 

 589071b7d13946009586524765353b38.jpeg

情形2:

8208fe66ff7b4c8ab16c5616a0d7d222.jpeg

下图含义为,当dest在src前,就从前往后拷贝;当dest在src后,就从后往前拷贝;当src和dest没有重合,就即可从后往前又可以从前往后,笔者在下面的代码中只会尝试从后往前的情况 

3f063a4f53004aaaa175db824c3934df.jpeg

void my_memmove(void* dest, void* src, unsigned int num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src) //存放地址的顺序前后比较,数组是连续存放,存放在低地址处(下标较小处)的顺序靠前,存放在高地址处(下标较大处)的顺序靠后,顺序越靠前越小,越靠后越大
	{
		//从前往后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			src = (char*)src + 1;
			dest = (char*)dest + 1; //从前向后的方式上文已经分析过,此处略
		}
	}
	else
	{
		//从后往前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
			//从后往前,一开始是在最前面的,加上num就变成最后了,随着num减小,两者也在跟着往低地址处跑
		}
	}
	return ret;
}

 

三.memset函数

d0d451206275445289d052eef311fbd4.png

void* ptr:指向要填充的内存块的指针

int value:要设置的值。该值作为 int 传递,但该函数使用此值的无符号字符转换填充内存块

size_t num:要设置为该值的字节数

72e424573af347aa9d73fc06a2222888.jpg

但请注意,对于整型来说,memset函数是以字节为单位进行存放的

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

上述代码最终打印结果为16843009,这是因为memset函数是以字节为单位进行存放,在1个字节当中,10进制的1的16进制表示为:01,如下图所示。一个字节一个字节以01的16进制方式存入内存,而一个整型占四个字节,所以最后输出的结果为16进制下的01 01 01 01,因此才会输出非常大的一个结果

2a31b8901ddc478bbb4fbe7f64f64b4f.jpg

四.memcmp函数

8659089470264b308084db37ea283c6b.png

num即为所需比较的字节数量,且memcmp函数是以字节为单位进行比较的(下文有具体讲解)

该函数就是用来完成内存块比较的。返回值分为正、负、0三种情况;正为大,负为小,0为相等(与strncmp函数返回值含义相同,且非常相似,区别就在于一个可以面向所有类型数据,一个只是面向了字符串与字符型数据)

 

memcmp函数是以字节为单位进行比较的,下面笔者进行讲解:

int arr1[] = { 1,2,3,4,5,6,7,8 };
int arr2[] = { 1,2,3,4,8,8,8,8 };
int ret = memcmp(arr1, arr2, 17);
printf("%d", ret);

在vscode2022编译器下,最终结果为-1。

因为在vscode编译器里是以小端存储(相关文章链接:数据存储),因此小的数字放在高位;而比较17个字节最终的比较是 05和08 这两个以16进制表示的一字节,自然而然就有arr2的数据要比arr1大的结果

1fac8213d5494a3eba2a20dedae04c07.jpg

 

 

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

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

相关文章

C++中的右值引用和移动语义

1 左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 什么是左值&#xff1f;什么是左值引用&#xff1f; 左…

初探 Google 云原生的CICD - CloudBuild

大纲 Google Cloud Build 简介 Google Cloud Build&#xff08;谷歌云构建&#xff09;是谷歌云平台&#xff08;Google Cloud Platform&#xff0c;GCP&#xff09;提供的一项服务&#xff0c;可帮助开发人员以一致和自动化的方式构建、测试和部署应用程序或构件。它为构建和…

【Java探索之旅】包管理精粹 Java中包的概念与实践

文章目录 &#x1f4d1;前言一、封装1.1 封装的概念1.2 访问限定修饰符 二、封装扩展&#xff08;包&#xff09;2.1 包的概念2.2 带入包中的类2.3 自定义包2.4 常见的包 &#x1f324;️全篇总结 &#x1f4d1;前言 在Java编程中&#xff0c;封装是面向对象编程的核心概念之一…

PyCharm 2024新版图文安装教程(python环境搭建+PyCharm安装+运行测试+汉化+背景图设置)

名人说&#xff1a;一点浩然气&#xff0c;千里快哉风。—— 苏轼《水调歌头》 创作者&#xff1a;Code_流苏(CSDN) 目录 一、Python环境搭建二、PyCharm下载及安装三、解释器配置及项目测试四、PyCharm汉化五、背景图设置 很高兴你打开了这篇博客&#xff0c;如有疑问&#x…

Swift - 可选项(Optional)

文章目录 Swift - 可选项&#xff08;Optional&#xff09;1. 可选项&#xff08;Optional&#xff09;2. 强制解包&#xff08;Forced Unwrapping&#xff09;3. 判断可选项是否包含值4. 可选项绑定&#xff08;Optional Binding&#xff09;5. 等价写法6. while循环中使用可选…

【linuxC语言】stat函数

文章目录 前言一、stat函数二、示例代码总结 前言 在Linux系统编程中&#xff0c;stat() 函数是一个非常重要的工具&#xff0c;用于获取文件的元数据信息。无论是在系统管理、文件处理还是应用开发中&#xff0c;都可能会用到 stat() 函数。通过调用 stat() 函数&#xff0c;…

简约大气的全屏背景壁纸导航网源码(免费)

简约大气的全屏背景壁纸导航网模板 效果图部分代码领取源码下期更新预报 效果图 部分代码 <!DOCTYPE html> <html lang"zh-CN"> <!--版权归孤独 --> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible…

用LM Studio搭建微软的PHI3小型语言模型

什么是 Microsoft Phi-3 小语言模型&#xff1f; 微软Phi-3 模型是目前功能最强大、最具成本效益的小型语言模型 &#xff08;SLM&#xff09;&#xff0c;在各种语言、推理、编码和数学基准测试中优于相同大小和更高大小的模型。此版本扩展了客户高质量模型的选择范围&#x…

C# Winform父窗体打开新的子窗体前,关闭其他子窗体

随着Winform项目越来越多&#xff0c;界面上显示的窗体越来越多&#xff0c;窗体管理变得更加繁琐。有时候我们要打开新窗体&#xff0c;然后关闭多余的其他窗体&#xff0c;这个时候如果一个一个去关闭就会变得很麻烦&#xff0c;而且可能还会出现遗漏的情况。这篇文章介绍了三…

Stylus深度解析:开发效率提升秘籍(AI写作)

首先&#xff0c;这篇文章是基于笔尖AI写作进行文章创作的&#xff0c;喜欢的宝子&#xff0c;也可以去体验下&#xff0c;解放双手&#xff0c;上班直接摸鱼~ 按照惯例&#xff0c;先介绍下这款笔尖AI写作&#xff0c;宝子也可以直接下滑跳过看正文~ 笔尖Ai写作&#xff1a;…

基于北京迅为iTOP-RK3588大语言模型部署测试

人工智能&#xff08;AI&#xff09;领域中的大模型&#xff08;Large Model&#xff09;逐渐成为研究的热点。大模型&#xff0c;顾名思义&#xff0c;是指拥有海量参数和高度复杂结构的深度学习模型。它的出现&#xff0c;不仅推动了AI技术的突破&#xff0c;更为各行各业带来…

社交媒体数据恢复:Sugram

Sugram数据恢复的方法 在本文中&#xff0c;我们将探讨Sugram数据恢复的基本方法。通过专业软件按照数据恢复步骤来了解如何进行数据恢复。 1. 立即停止使用设备 一旦发现数据丢失&#xff0c;第一步应该是立即停止使用该设备。这是因为每次设备被使用&#xff0c;都有可能导…

SpringBoot~ dubbo + zookeeper实现分布式开发的应用

配置服务名字, 注册中心地址, 扫描被注册的包 server.port8081 #当前应用名字 dubbo.application.nameprovider-server #注册中心地址 dubbo.registry.addresszookeeper://127.0.0.1:2181 #扫描指定包下服务 dubbo.scan.base-packagescom.demo.service 实现一个接口,在…

IoTDB 入门教程③——基于Linux系统快速安装启动和上手

文章目录 一、前文二、下载三、解压四、上传五、启动六、执行七、停止八、参考 一、前文 IoTDB入门教程——导读 二、下载 下载二进制可运行程序&#xff1a;https://dlcdn.apache.org/iotdb/1.3.1/apache-iotdb-1.3.1-all-bin.zip 历史版本下载&#xff1a;https://archive.…

ROS2专栏(三) | 理解ROS2的动作

​ 1. 创建一个动作 目标&#xff1a; 在ROS 2软件包中定义一个动作。 1.1 新建包 设置一个 workspace 并创建一个名为 action_tutorials_interfaces 的包&#xff1a; mkdir -p ros2_ws/src #you can reuse existing workspace with this naming convention cd ros2_ws/s…

STM32 工程移植 LVGL:一步一步完成

STM32 工程移植 LVGL&#xff1a;一步一步完成 LVGL&#xff0c;作为一款强大且灵活的开源图形库&#xff0c;专为嵌入式系统GUI设计而生&#xff0c;极大地简化了开发者在创建美观用户界面时的工作。作为一名初学者&#xff0c;小编正逐步深入探索LVGL的奥秘&#xff0c;并决…

52.HarmonyOS鸿蒙系统 App(ArkTS)配置文件添加多个权限方法

52.HarmonyOS鸿蒙系统 App(ArkTS)配置文件添加多个权限方法 module.json5

关于修改hosts,浏览器并没有刷新生效的问题.

1.windows系统用cmd命令: ipconfig /flushdns 进行刷新.并查看本地解析是否已经刷新. 2.检查是否开了,代理,代理还是有影响的,关闭,不然不会生效 3.针对谷歌浏览器解决方案: 访问: chrome://net-internals/?#sockets 点击close idle sockets和flush socket pools,,,清…

如何将安卓手机投屏到Windows 10电脑上

诸神缄默不语-个人CSDN博文目录 我之所以要干这个事是为了用手机直播的时候在电脑上看弹幕…… 文章目录 1. 方法一&#xff1a;直接用Win10内置的投影到此电脑2. 方法二&#xff1a;用AirDroid Cast投屏到电脑上 1. 方法一&#xff1a;直接用Win10内置的投影到此电脑 在设置…

Flutter笔记:Widgets Easier组件库(8)使用图片

Flutter笔记 Widgets Easier组件库&#xff08;8&#xff09;&#xff1a;使用图片 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…