【C语言】内存函数的概念,使用及模拟实现

news2024/11/21 2:30:10
Tiny Spark get dazzling some day.

目录

  • 1. memcpy
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 2.memmove
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 3. memset
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 4. memcmp
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现

1. memcpy

  • 使用需包含头文件:<string.h>

– 函数原型

#include <string.h> // 头文件
void* memcpy ( void* destination, const void* source, size_t num ); 
			       目标空间               源内容         拷贝数目

memcpy 函数的作用,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。

规则:

  1. 源指针目标指针 指向的对象的 基础类型 与此函数无关,即: memcpy函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
  2. memcpy 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
  3. 为避免溢出,两个参数 所指向的空间最好不是同一个(否则可能会造成重叠问题,这是更推荐使用的是 memmove 函数)。
  4. 确保 目标空间 有足够位置存放要复制的内容。

这哥们有点和 字符串函数的 strcpy 函数类似,都是把一段内容复制粘贴到另一个空间。不同的是 strcpy 只能复制 字符串内容, 而 memcpy 能复制任意类型的内容。


– 函数使用

int dest_arr[10]; 
int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
int sz = sizeof(src_arr) / sizeof(src_arr[0]);
memcpy(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9

memcpy 并没有到 ’ \0 '停止了而是继续复制,我理解为函数并没有去检测 ’ \0 ’ 的含义,而是仅仅将其当作数组元素一个数据而已。

我们再来看看第二种—复制字符串:

char dset_str[20];
char src_str[] = "Jackie\0Chan";
int sz = sizeof(src_str) / sizeof(src_str[0]);
memcpy(dest_str, src_str, sz * sizeof(char);// 把整个源数组的内容都复制过去
//此时 dest_arr[10] 里面的内容就是 Jackie\0Chan
printf("%s", dest_str);
// 但是,在 printf 打印的时候不要误跟着以为 也会不检测'\0'而打印 "Jackie\0Chan"
// 从而可能一脸懵逼 (me),或者 "这不常识?~不屑" (大佬you)

第三种–如果 源空间目标空间 重叠时使用情况:

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
memcpy(arr + 2, arr, 8 * sizeof(int));// 源空间开头为 第三个元素的位置 
for(int i= 0; i < 10; i++)
	printf("%d ",  arr[i]);

在VS2022上可能直接用可能会正常打印 1 2 1 2 3 4 5 6 7 8
但如果用 模拟函数实现的方法 执行程序:
在这里插入图片描述

这种是错误的
下面来解释下:
在这里插入图片描述
所以,若接着往下走,最后的输出结果就是

1 2 1 2 1 2 1 2 1 2

所以在使用 memcpy 函数时尽量不要 将 目标指针源指针 指向同一块空间。


– 函数的模拟实现

#include <assert.h>
void* Sim_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest);
	assert(src);
	//先检测 dest 和 src 是否为空指针
	while (num--) 
	{
		*(char*)dest = *(char*)src;// 非常精细,一个字节一个字节地复制
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
 return(ret);
}

2.memmove

  • 使用需包含头文件:<string.h>

– 函数原型

void* memmove ( void* destination, const void* source, size_t num );
                    目标空间				  源内容         拷贝数目

memmove 函数的作用 和 memcpy 基本相同,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。
但是, memcpy 不能作用在 同一块空间, 也就是无法在空间重叠下复制,而 memmove 可以解决这个问题。

规则:

  1. 源指针目标指针 指向的对象的 基础类型 与此函数无关,即: memmove函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
  2. memmove 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
  3. memmove在拷贝时会有一个 缓冲区 来接受源空间,所以允许 源空间目标空间 重叠。
    4。 确保 目标空间 有足够的位置来存放复制的内容。

– 函数使用

第一种使用情况和 memmove 基本相同

int dest_arr[10]; 
int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
int sz = sizeof(src_arr) / sizeof(src_arr[0]);
memmove(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9

第二种—如果 源空间 和 目标空间 重叠时使用情况:

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

结果是:

1 2 1 2 3 4 5 6 7 8

那为什么 memcpy 无法满足实现空间重叠的情况,而 memmove 却可以呢?
在这里插入图片描述
所以,在往后遇到 目标指针源指针 指向的空间发生空间重叠时,可以使用 memmove 函数来解决。

– 函数的模拟实现

#include <assert.h>
void* Sim_memmove(void* dest, const void* src, size_t num)
{
 	void* ret = dest;assert(dest);
	assert(src);
	//先检测 dest 和 src 是否为空指针
  	if (dest <= src || (char*)dest >= ((char*)src + num)) // 从前往后拷
  	{
    	while (num--) 
    	{
        	*(char*)dest = *(char*)src;
        	dest = (char*)dest + 1;
        	src = (char*)src + 1;
 		}
 	}
  	else // 从后往前拷
  	{
    	dest = (char*)dest + count - 1;
    	src = (char*)src + count - 1;
    	while (numt--) 
    	{
        	*(char*)dest = *(char*)src;
        	dest = (char*)dest - 1;
        	src = (char*)src - 1;
    	}
    }
  	return(ret);
}

有点小难理解,下面我们来看看:

首先定义一个数组:int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

第一种情况:

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

`在这里插入图片描述
第二种情况:

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

在这里插入图片描述
所以在模拟实现 memmove函数时,要确定拷贝顺序是 从前往后 还是 从后往前
在这里插入图片描述


3. memset

  • 使用需包含头文件:<string.h>

– 函数原型

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

memset 函数用来将 ptr 指向的内存块的的指定范围(num个 字节)设置为指定值。

– 函数使用

char str[] = "Hello World";
memset(str, 'X', 5);
printf("%s", str);

输出结果:

XXXXX World

– 函数的模拟实现

#include <assert.h>
void* Sim_memset(void* ptr, int value, size_t num)
{
	void* ret = ptr;
	assert(ptr != NULL);
	// 先检测 ptr 是否为空指针
	while (num--)
	{
		*(char*)ptr = value;
		(char*)ptr += 1;
	}
	return ret;
}

4. memcmp

  • 使用需包含头文件:<string.h>

– 函数原型

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

memcmp 函数用来比较 ptr1 指向的内存块前 num个字节 的内容和 **ptr2 ** 指向的内存块的前 num个字节 的内容。

规则:

  1. 该函数和 strcmp 相似,都是比较函数,但是 memcmp 函数在找到 ’ \0 ’ 字符后不会停止比较。
  2. 该函数会返回一个整形值,该值指示的内存块的内容之间关系如下:
返回值表明
<0在两个内存块中不匹配的第一个字节在 ptr1 中的值低于 ptr2 中的值
=0两个内存块的内容相等
>0在两个内存块中不匹配的第一个字节在 ptr1 中的值大于 ptr2 中的值

– 函数使用

比较两个数组

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,11 };
int ret = memcmp(arr1, arr2, 10 * sizeof(int));
if (ret < 0)
    printf("arr1 < arr2");
else if (ret == 0)
    printf("arr1 == arr2");
else
    printf("arr1 > arr2");

输出结果:

arr1 < arr2

比较两个字符串

char* str1 = "ABCDE";
char* str2 = "ABCDW";
int ret = memcmp(str1, str2, 10 * sizeof(char));
if (ret < 0)
    printf("arr1 < arr2");
else if (ret == 0)
    printf("arr1 == arr2");
else
    printf("arr1 > arr2");

输出结果

arr1 < arr2

– 函数的模拟实现

int Sim_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
    assert(ptr1 != NULL);
    assert(ptr2 != NULL);
    // 先判断 ptr1 和 ptr2 是否为空指针
    while ((*(char*)ptr1 == *(char*)ptr2) && num)
    {
        ((char*)ptr1)++;
        ((char*)ptr2)++;
    }
    return *(char*)ptr1 - *(char*)ptr2;// 返回该字节内容的对应的 ASCII码表值的 差值
}

该函数的模拟实现和 strcmp 的函数模拟实现有点相似,不同的是,strcmp 需要实现遇到
’ \0 ’ 时便停止比较,而 memcmp 则是直接忽略 ’ \0 ’ 继续比较(字符若为 ’ \0 ’ 也会执行一次比较) 。

  
  
  Stay hungry. Stay Foolish. 饥渴求知,虚怀若愚。
  感谢各位读者支持,虚心请教,如有错漏或可改进点,请任意指出,感激不尽!
  一起进步!


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

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

相关文章

3dmax-vray6渲染器参数设置

适用于3dmax2018-2023版本 一、【公用】 小图输出大小:1500*1125&#xff0c;勾选大气、效果&#xff1b; 大图输出大小:3000*2250&#xff0c;勾选大气、效果、置换&#xff1b; 二、【vray】 小图抗锯齿类型:渐进式&#xff1b;最小细分:1&#xff0c;最大细分:100&#…

C++新特性

1 智能指针 1.1 为什么需要智能指针 智能指针主要解决以下问题&#xff1a; 内存泄漏&#xff1a;内存手动释放&#xff0c;使用智能指针可以自动释放共享所有权指针的传播和释放&#xff0c;比如多线程使用同一个对象时析构问题&#xff0c;比如&#xff1a; C里面的四个智…

解决springboot项目的网站静态页面显示不全问题

在通过springboot搭建项目时&#xff0c;为了能够访问静态的前端页面&#xff0c;我们考虑到访问的优先级问题&#xff0c;通常选择将资源放在recourses/static的目录下&#xff0c;如下&#xff1a; 这时可能会出现类似于下面这种图片无法加载、没有按照指定位置显示的情况&am…

R语言:r画韦恩图

> setwd("") > library(openxlsx) > library(ggvenn) > data <- read.xlsx("韦恩图种2.xlsx") data$P <- ifelse(data$P 0, "F", "T") data$N <- ifelse(data$N 0, "F", "T")> data &l…

流星烛台如何交易?Anzo Capital昂首资本3步盈利收场

各位投资者通过之前的文章可以准确的辨认出什么是流星烛台了&#xff0c;但是各位投资者一旦遇到流星图案知道怎么交易吗?其实一点都不困难&#xff0c;只要掌握住流星图案的交易真棒&#xff0c;Anzo Capital昂首资本3步就可以盈利收场。 首先&#xff0c;投资者需要确定图…

技术速递|介绍 .NET MAUI 社区工具包 v8 :包含 TouchBehavior 支持!

作者&#xff1a;Gerald Versluis 排版&#xff1a;Alan Wang .NET MAUI 社区工具包团队很自豪地向您介绍 .NET MAUI 社区工具包的第 8 版&#xff01; 在这个最新的主要版本中&#xff0c;我们为您带来了备受期待的 TouchBehavior&#xff08;以前称为 TouchEffect&#xff0…

【C++】详解STL容器之一的 vector

目录 概述 迭代器 数据结构 优点和缺点 接口介绍 begin end rbegin rend resize reseve insert erase 其他一些接口 模拟实现 框架 获取迭代器 深浅拷贝 赋值重载 reseve resize 拷贝构造 构造 析构 insert erase 其他 概述 vector是STL的容器之一。…

二叉树遍历总结

7.二叉树 二叉树理论基础 二叉树的种类 在我们解题过程中二叉树有两种主要的形式&#xff1a;满二叉树和完全二叉树。 满二叉树 完全二叉树 二叉搜索树 平衡二叉搜索树 C中map、set、multimap&#xff0c;multiset的底层实现都是平衡二叉搜索树&#xff0c;所以map、set的增…

2024.5.8 2.二叉树的最大深度 (简单)

给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;root [1,null,2] 输…

GZIP文件格式解析和Inflate静态Huffman解压缩

GZIP是封装了Deflate压缩的格式文件&#xff1b;Deflate使用了无压缩、HuffmanLZ77进行压缩&#xff1b;解压是Inflate&#xff0c;Huffman包括静态Huffman压缩和动态Huffman压缩两种模式。 Java语言实现了GZIP格式解析、Inflate的静态Huffman解压缩、CRC32校验 算法。 gzip文…

7-AMCA Mal,可通过与蛋白质上的巯基反应生成具有荧光的标记物

【产品概述】 7-AMCA Mal&#xff0c;也被称为7-AMCA maleimide&#xff0c;是一种在生物医学领域中常用的荧光染料。 中文名称&#xff1a;7-AMCA 马来酰亚胺 英文名称&#xff1a;7-AMCA Mal&#xff0c;7-AMCA maleimide CAS号&#xff1a;N/A 分子式&#xff1a;C18H17N3…

STEP BY STEP带你使用Docker搭建MySql-MGR高可用集群

数据的重要性 数据已成为当今数字时代最重要的资产之一&#xff0c;对于企业的成功至关重要。它可以帮助企业了解客户、市场和自身运营&#xff0c;提高运营效率&#xff0c;做出明智决策&#xff0c;推动创新&#xff0c;并获得竞争优势。 数据的采集&#xff0c;存储&#…

解析源代码安全的防泄密解决途径

随着各行各业业务数据信息化发展&#xff0c;各类产品研发及设计等行业&#xff0c;都有关乎自身发展的核心数据&#xff0c;包括业务数据、代码数据、机密文档、用户数据等敏感信息&#xff0c;这些信息数据有以下共性&#xff1a; 属于核心机密资料&#xff0c;万一泄密会对…

TCP四次挥手中为什么 TIME_WAIT 等待的时间是 2MSL?

TCP 连接断开 1、TCP 四次挥手过程是怎样的&#xff1f;如下图 2、为什么 TIME_WAIT 等待的时间是 2MSL&#xff1f; MSL 是 Maximum Segment Lifetime&#xff0c;报文最大生存时间&#xff0c;它是任何报文在网络上存在的最长时间&#xff0c;超过这个时间报文将被丢弃。因…

JavaEE技术之MySql高级-ShardingSphere5(SpringBoot版本:3.0.5)

文章目录 1 ShardingSphere-JDBC读写分离1.1 创建SpringBoot程序1.1.1、创建项目1.1.2、添加依赖1.1.3、创建实体类1.1.4、创建Mapper1.1.5、配置 Spring Boot1.1.6、配置shardingsphere 1.2 测试1.2.1 读写分离测试1.2.2 负载均衡测试1.2.3 事务测试常见错误 2 ShardingSphere…

电子商务对应的职业有哪些?10年互联网人透底行业秘密!

电子商务对应的职业有哪些&#xff1f;10年互联网人透底行业秘密&#xff01; 事实说话&#xff0c;实事求是&#xff0c;不要再把美颜滤镜下的市场&#xff0c;传给新人小伙伴了&#xff01; 大家好&#xff0c;我是微三云胡佳东&#xff0c;一家软件公司负责人&#xff01; …

20240508在RK3588的Buildroot系统下播放MP4视频

20240508在RK3588的Buildroot系统下播放MP4视频 2024/5/8 18:09 开发板&#xff1a;飞凌的OK3588-C SDK&#xff1a;Linux/Buildroot R4版本 4.4.2.5 播放 H264 格式视频 [rootok3588:/]# gst-launch-1.0 filesrc location13850_h264.mp4 ! qtdemux ! queue ! h264parse ! mpp…

3D模型素材有哪些常见的用途?

3D模型素材已经成为了设计、游戏开发、电影制作和建筑等领域的重要工具。它们以其独特的形式和丰富的细节&#xff0c;为这些领域的专业人士提供了无尽的创作可能性。 1.建筑和室内设计&#xff1a;在建筑设计中&#xff0c;3D模型可以帮助建筑师更直观地展示设计方案&#xff…

WiFi原理

一、引言 简介&#xff1a;WiFi&#xff0c;全称Wireless Fidelity&#xff0c;是一种允许电子设备连接到一个无线局域网&#xff08;WLAN&#xff09;的技术&#xff0c;通常使用2.4GHz或5GHz UHF&#xff08;特高频&#xff09;和SHF&#xff08;超高频&#xff09;ISM&…

游戏辅助 -- 三种分析角色坐标方法(CE、xdbg、龙龙遍历工具)

所用工具下载地址&#xff1a; https://pan.quark.cn/s/d54e7cdc55e6 在上次课程中&#xff0c;我们成功获取了人物对象的基址&#xff1a;[[[0xd75db8]1C]28]&#xff0c;而人物血量的地址则是基址再加上偏移量278。 接下来&#xff0c;我们需要执行以下步骤来进一步操作&a…