C语言内存函数

news2024/12/30 1:27:09

目录

  • memcpy(Copy block of memory)使用和模拟实现
    • memcpy的模拟实现
  • memmove(Move block of memory)使用和模拟实现
    • memmove的模拟实现:
  • memset(Fill block of memory)函数的使用
    • 扩展
  • memcmp(Compare two blocks of memory)函数的使用

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒个人主页
🥸🥸🥸C语言
🐿️🐿️🐿️C语言例题
🐣🐓🏀python

memcpy(Copy block of memory)使用和模拟实现

代码格式:

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

memcpy使用要点:
1:函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置

2:这个函数在遇到 ‘\0’ 的时候并不会停下来

3:如果source和destination有任何的重叠,复制的结果都是未定义的

4:由于不知道程序猿会传入什么样的指针,所以我们用void * 表示各种类型的指针,因此我们用void*后可以传入int * ,char * …类型的指针

此外我们还有区分 strcpy和memcpy,一个是拷贝字符,一个是拷贝内存,memcpy拷贝是针对不重叠的拷贝)
我们解释一下重叠的含义:

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

这就是拷贝重叠,arr+2是表示的&a[2],arr是数组名表示首元素的地址,我们用一个图来说明
在这里插入图片描述

首先我们将a[0]=0,a[1]=1分别替换a[2]=2,a[3]=3,由于传进去的是地址,所以是永久改变,此时我们的a[2]和a[3]都发生了变化,但是a[2]和a[3]还没有去替换a[4]和a[5]

当我们用a[2]和a[3]去替换a[4]和a[5]时,a[2]和a[3]已经变成了0和1,所以替换a[4]和a[5]的结果也是0和1

只不过有些编译器的结果并不是这样的(比如VS),因为VS中的memcpy约等于memmove,唯一的解释就是程序猿在实现memcpy时的编写不同,所以代码的结果就不同,但是大多数编译器都是前面的结果

代码示例:

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

代码解析:
这里有两个int类型的数组,arr2中的元素全是0,我们将arr1中的元素复制到arr2中

由于我们传入的arr2和arr1都是数组的数组名,也就是数组首元素的地址,传入的字节数为20,因为一个数组中的元素大小为4个字节,所以相当于传入5个元素到arr2中

之后就从arr2中第一个元素0开始改变,总共改变5个元素,打印的结果如下
在这里插入图片描述

memcpy的模拟实现

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

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

代码解析:
因为是拷贝的内存,但是数组中的元素是整形元素,如果我们拷贝的内存大小不是4的倍数(一个整形大小为4个字节),比如7,那么可能就无法拷贝进去

因此我们需要将传入的数组元素地址进行强制类型转换成char,只有这样才能一个字节一个字节的拷贝, * (char)dest = * (char*)src是将dest和src强制转换为char*类型的指针,再解引用,将src的一个字节拷贝给dest**

而dest = (char)dest + 1和src = (char)src + 1都是将两个数组强制类型转换,再通过+1跳到下一个字节,这样我们就可以保证每一个字节都能够拷贝**

错误代码示例1:
void* my_memcpy(void* dest,const void* src, size_t sz)
{
	assert(dest && src);
	while (sz--)
	{
		*(char*)dest = *(char*)src;
		 (char*)dest++;
		(char*)src++;
	}
	return dest;
}
错误代码示例2:
void* my_memcpy(void* dest,const void* src, size_t sz)
{
	assert(dest && src);
	while (sz--)
	{
		*(char*)dest = *(char*)src;
		dest = ++(char*)dest ;
		src = ++(char*)src ;
	}
	return dest;
}

错误1具体原因其实我也没有搞懂,错误2只能在某些编译器下可以正常运行,但不是全部,因此就算作错误

memmove(Move block of memory)使用和模拟实现

代码格式:

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

memmove使用要点:

1:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的

2:如果源空间和目标空间出现重叠,就得使用memmove函数处理

代码示例:

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

代码解析:
这里只有一个数组arr1,我们传入的地址为arr1+2和arr1,分别表示数组第三个元素的地址(&arr1[3]),和数组首元素的地址

在这里插入图片描述

memmove的模拟实现:

在实现之前我们先理解一下思路:
memmove的实现其实有一种比较简单的方法,就是我们只需要创造一个和arr一模一样的数组brr,由于是两个不同的数组,内存地址是不同的,只是数组元素是相同的,这样我们就解决重叠的问题了
在这里插入图片描述
但是我们能不能只通过arr自身而不去创造brr去实现memmove呢?
在这里插入图片描述
如图:我们需要将绿色方框中的元素拷贝到红色方框中的元素,可以像这样拷贝,我们先将a[4]=3拷贝到a[6]=5中,然后将a[3]=2拷贝到a[5]=4中…这样就可以避免重叠拷贝,造成拷贝元素不是我们想要的情况了

而如果我们需要将图中绿色方框中的元素拷贝到前面红色方框的元素呢?
在这里插入图片描述
其实方式都是一样的

综上如果我们只用数组arr而不创造数组brr的话,我们需要考虑到两种情况,一个是往前拷贝,一个是往后拷贝,所以针对情况我们就一定会用到if语句

具体实现代码如下:

void* my_memmove(void* dest, const void* src, size_t sz)
{
	assert(dest && src);
	void* ret = dest;//记录dest启始地址
	if (dest < src)//从前向后
	{
		for (int i = 0; i < sz; i++)
		{
			*(char*)dest = *(char*)src;
				dest = (char*)+1;
				src = (char*)src + 1;
		}
	}
	else//从后向前
	{
		while (sz--)
		{
			*((char*)dest + sz) = *((char*)src + sz);
		}
	}
	return ret;
}

代码解析:
* (char * )dest = * (char*)src是将dest和src强制转换为char*,方便后续可以一个字节一个字节的拷贝

dest = (char*)+1和src = (char*)src + 1是将两个数组都强转后向后移动一个字节

由于while(sz–)是先判断sz是否=0,判断完后再执行后置减减,然后在通过 *((char *)dest + sz) = * ((char * )src + sz)确定需要拷贝的数组和要拷贝数字的位置,我们用一个图来说明:

我们需要将绿色方框中的元素拷贝进红色方框中去,下面是各元素的16进制表示
在这里插入图片描述
在这里插入图片描述

memset(Fill block of memory)函数的使用

代码格式:

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

memset使用要点:
1:memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容

代码示例:

#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "hello world";
	memset(str, 'x', 6);
	printf("%s",str);
	return 0;
}

代码解析:
memset中我们传入的
在这里插入图片描述

扩展

memset是以字节为单位设置内存的,为了方便理解我们看一个代码

int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 40);
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", arr[i]);
}
	return 0;
}

运行结果如图:
在这里插入图片描述
我们调试看一下内存
在这里插入图片描述
这是以16进制的方式存储的,在还没有执行memset时内存存储的每个字节都是0

在这里插入图片描述
而执行memset后我们可以看到每隔一个字节都会有一个0变成1,因此我们通过这个就比较容易理解以字节为单位设置内存这句话了

所以memset最好还是设置char类型的数组

memcmp(Compare two blocks of memory)函数的使用

代码格式:

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

memcmp使用要点:
1:比较从ptr1和ptr2指针指向的位置开始,向后的num个字节

2:memcmp是比较两个内存块

返回值如下
在这里插入图片描述
代码示例:

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	//01 00 00 00  02 00 00 00  03 00 00 00 04 00 00 00  05 00 00 00...
	int arr2[] = { 1,2,3 };
	//01 00 00 00  02 00 00 00  03 00 00 00
	int a = memcmp(arr1, arr2, 12);
	printf("%d", a);
	return 0;
}

代码结果如下:
在这里插入图片描述
但如果我们稍微改一下代码呢?

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	//01 00 00 00  02 00 00 00  03 00 00 00 04 00 00 00  05 00 00 00...
	int arr2[] = { 1,2,3,0x11223304 };
	//01 00 00 00  02 00 00 00  03 00 00 00  04 33 22 11
	int a = memcmp(arr1, arr2, 13);
	printf("%d", a);
	return 0;
}

可以看到我们将arr2中添加了一个16进制的元素0x11223304 ,并且在memcmp中我们将比较的12个字节改为13个字节,结果如下
在这里插入图片描述
结果是0,从这里我们也可以看出,memcmp也是将数组中的元素用16进制来表示,再通过一个字节一个字节的比较,方式有点像前面的memset

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

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

相关文章

JavaScript小游戏实现高分榜

之前我们实现了游戏的主体功能&#xff0c;以及恢复之类的功能&#xff01;本次我们来实现高分榜的功能 功能的需求如下&#xff1a; ● 这个逻辑非常的简单&#xff0c;就是当我们猜对了数字后&#xff0c;跟我们的highscore做比较&#xff0c;如果比highscore高的话&#x…

跨境电商系统对接-进口

一、跨境进口方式 1、一般贸易 指中国境内有进出口经营权的企业进行进出口贸易&#xff0c;货物到港后需要先清关&#xff08;办理海关申报、查验、征税、放行等手续&#xff09;&#xff0c;然后货主才能提货&#xff0c;一般贸易适合大批量进口商品&#xff0c;公司的鲜奶、…

面试题:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

文章目录 前言时间片超线程上下文切换切换查看线程调度引起线程上下文切换的因素总结 前言 由于现在大多计算机都是多核CPU&#xff0c;多线程往往会比单线程更快&#xff0c;更能够提高并发&#xff0c;但提高并发并不意味着启动更多的线程来执行。更多的线程意味着线程创建销…

厌烦了iPhone默认的热点名称?如何更改iPhone上的热点名称

你对你默认的热点名称感到厌倦了吗&#xff1f;这篇文章是为你准备的。在这里&#xff0c;你可以了解如何轻松更改iPhone上的热点名称。 个人热点会将你的手机数据转换为Wi-Fi信号。手机上的个人热点使用户能够与其他用户共享其蜂窝数据连接。当你在WIFI网络之外时&#xff0c…

更轻便使用Siri!iOS 17让你用Siri的效率倍增

安装iOS 17后&#xff0c;你可以学习如何连续发出一个接一个的Siri请求。只要你有iPhone 11或iPhone SE第二代或更高版本&#xff0c;你就不需要不断重新激活Siri来提出后续请求。你只需要继续说下去。 Siri聪明的是&#xff0c;它可以了解你所问的要点。例如&#xff0c;你可…

根据中序与后序遍历结果构造二叉树

文章前言&#xff1a;对于中序与后序遍历不是太清楚的小白同学&#xff0c;作者推荐&#xff1a; 二叉树的初步认识_加瓦不加班的博客-CSDN博客 解题思路&#xff1a; 先通过后序遍历结果定位根节点 再结合中序遍历结果切分左右子树 代码实现&#xff1a; //1. pre-order 前…

让照片人物开口说话,SadTalker 安装及使用(避坑指南)

AI技术突飞猛进&#xff0c;不断的改变着人们的工作和生活。数字人直播作为新兴形式&#xff0c;必将成为未来趋势&#xff0c;具有巨大的、广阔的、惊人的市场前景。它将不断融合创新技术和跨界合作&#xff0c;提供更具个性化和多样化的互动体验&#xff0c;成为未来的一种趋…

如何轻松正确的写出链表代码?

文章来源于极客时间前google工程师−王争专栏。 技巧一&#xff1a;理解指针或引用的含义 指针&#xff1a;存储所指对象的内存地址 将某个变量赋值给指针&#xff0c;实际上就是将这个变量的地址赋值给指针&#xff0c;或者反过来说&#xff0c;指针中存储了这个变量的内存地…

【WIN】双机调试——Net模式

概述&#xff1a;windows 双机调试可以在主页搜索 bcdedit 命令。 参考&#xff1a; Set up KDNET network kernel debugging manually - Windows drivers | Microsoft Learn 双机调试是一个非常有用的技术&#xff0c;方便内核、驱动等调试场景。 本文主要记录个人尝试使用ne…

2023年中国建筑安装行业市场现状分析:占比建筑业总产值8.4%[图]

建筑业是我国经济支柱产业之一。建筑业包括房屋建筑业、土木工程建筑业、建筑安装业、建筑装修装饰和其他建筑业。建筑安装行业是建筑业的一个细分行业&#xff0c;其主要包括线路安装、管道安装、设备安装三个细分行业&#xff0c;具体是指建筑物主题工程竣工后&#xff0c;建…

pg_rman 的编译和使用

一、编译 下载地址&#xff1a; ossc-db/pg_rman: Backup and restore management tool for PostgreSQL (github.com)https://github.com/ossc-db/pg_rman代码由日本电信的运维团队维护。 针对不同的PostgreSQL版本&#xff0c;使用不同的分支编译&#xff0c;我的PG版本试1…

【 构建maven工程时,配置了阿里云的前提下,依旧使用中央仓库下载依赖导致失败的问题!】

构建maven工程时&#xff0c;配置了阿里云的前提下&#xff0c;依旧使用中央仓库下载依赖导致失败的问题&#xff01;&#xff01;&#xff01; 错误提示信息: Cannot download ZIP distribution from https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3…

Knife4j_接口概述、常用注解详解、搭建swagger项目、功能概述

文章目录 ①. knife4j的概述②. knife4j核心功能③. 从0开始搭建knife4j项目④. 常用注解 - Api④. ApiOperation注解⑤. ApiModelProperty注解⑥. ApiImplicitParam注解⑦. ApiImplicitParams注解⑧. 限制请求方式⑨. 导出离线API文档 ①. knife4j的概述 ①. knife4j是为Java …

C#上位机——根据命令发送

C#上位机——根据命令发送 第一步&#xff1a;设置窗口的布局 第二步&#xff1a;设置各个属性 第三步&#xff1a;编写各个模块之间的关系

【力扣】单调栈:901. 股票价格跨度

【力扣】单调栈&#xff1a;901. 股票价格跨度 文章目录 【力扣】单调栈&#xff1a;901. 股票价格跨度1. 题目介绍2. 思路3. 解题代码参考 1. 题目介绍 设计一个算法收集某些股票的每日报价&#xff0c;并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格…

文献综述|CV领域神经网络水印发展综述

前言&#xff1a;最近接触了「模型水印」这一研究领域&#xff0c;阅读几篇综述之后&#xff0c;大致了解了本领域的研究现状&#xff0c;本文就来总结一下该领域的一些基础知识&#xff0c;以飨读者。 ⚠️注&#xff1a;本文中出现的研究工作均基于计算机视觉任务开展&#x…

Git 学习笔记 | 安装 Git 及环境配置

Git 学习笔记 | 安装 Git 及环境配置 Git 学习笔记 | 安装 Git 及环境配置安装 Git配置 Git查看配置 Git 学习笔记 | 安装 Git 及环境配置 安装 Git 官方网站&#xff1a;https://git-scm.com/ 官网下载太慢&#xff0c;我们可以使用淘宝镜像下载&#xff1a;https://regist…

自学接口测试系列 —— 自动化测试用例设计基础!

一、接口测试思路总结 ❓首先我们在进行接口测试设计前思考一个问题&#xff1a;接口测试&#xff0c;测试的是什么&#xff1f; ❗我们必须要知道&#xff0c;接口测试的本质&#xff1a;是根据接口的参数&#xff0c;设计输入数据&#xff0c;验证接口的返回值。 那么接口…

Fast DDS之Transport

目录 transport层负责为DDS用户数据收发和服务发现提供通信。包含UDP&#xff0c;TCP&#xff0c;SHM。

Python——— 模块

&#xff08;一&#xff09;模块化(module)程序设计理念 模块和包概念的进化史 模块和包概念的进化史 ① Python程序由模块组成。一个模块对应 python 源文件&#xff0c;一般后缀名是&#xff1a; .py ② 模块由语句组成。运行Python 程序时&#xff0c;按照模块中语句的顺…