C语言内存操控的艺术探索:踏足四大秘境,铸就内存管理之巅峰传奇

欢迎来到白刘的领域   Miracle_86.-CSDN博客

系列专栏  C语言知识

先赞后看,已成习惯

   创作不易,多多支持!

在C语言的内存管理领域,四大秘境之一的内存操作函数无疑为程序员提供了强大的工具。这些函数——memcpy、memmove、memset、memcmp——各自拥有独特的用途和特性,它们在内存操控中扮演着至关重要的角色。


目录

一、memcpy——内存的拷贝艺术

1.1 memcpy的使用

1.2 memcpy的模拟实现

1.3 重叠问题

二、memmove——内存的移动魔法

2.1 memmove的使用

2.2 memmove的模拟实现

 三、memset——内存的初始化神器

 四、memcmp——内存的比较之术


一、memcpy——内存的拷贝艺术

在前面我们讲了许多字符串函数:

C语言字符函数与字符串函数:编织文字的舞会之梦(上)-CSDN博客

C语言字符函数与字符串函数:编织文字的舞会之梦(下)-CSDN博客

其中我们发现strcpymemcpy非常相似,我们了解了str代表字符串,cpy代表copy,也就是拷贝,那mem是什么呢?其实它是memory的简写,memory我们都知道是记忆的意思,而它还有一个意思——内存。可以简单理解为计算机的记忆,那不就是内存嘛。那我们可以推断出来,memcpy,它的作用是用来拷贝内存的。

1.1 memcpy的使用

它的原型如下:

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

不难看出,它和strncpy的参数是一模一样的,没错,正是如此,只不过它可以copy任意类型的,而strncpy只能拷贝字符串。

1.原理:memcpy从source的位置开始,向后复制num个字节,到destination指向的内存的位置。

2.与strncpy不同的是,mem在遇到'\0'时不会停止。

3.使用需要包含头文件<string.h>。

#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;
}

运行结果:

1.2 memcpy的模拟实现

聊完它的基本原理以及使用后,我们再来看看它的模拟实现。

首先通过参数来看,我们传进去一个目的地,一个源头,还有字节数,那我可以这么写:

void* memcpy(void* dst, const void* src, size_t count)

接下来我们可以使用循环,次数为count,由于count是字节数,所以我们这里一个字节一个字节进行拷贝会比较容易操作。而void*类型的指针不可以解引用,我们需要进行强制类型转换,将其转换成char*,代码如下:

void* memcpy(void* dst, const void* src, size_t count)
{void* ret = dst;assert(dst);assert(src);/** copy from lower addresses to higher addresses*/while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}return(ret);
}

 这里返回ret(目标空间的首元素地址)是因为在memcpy中返回的也是这个:

但是问题出现了,如果目标空间和源头重叠了怎么办? 

1.3 重叠问题

我们来看下面一段代码:

#include <stdio.h>
#include <string.h>
#include<assert.h>
void* my_memcpy(void* dst, const void* src, size_t count)
{void* ret = dst;assert(dst);assert(src);/** copy from lower addresses to higher addresses*/while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}return(ret);
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };my_memcpy(arr1+2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

我们可以看到目标空间与源头有重叠的部分,本来我们想把1,2,3,4,5拷贝到3,4,5,6,7的位置。

预期结果:1,2,1,2,3,4,5,8,9,10 

实际结果:

这是为什么呢?这是因为在拷贝3的时候,3的位置已经是1了,3被覆盖掉了,其他的同理。这可怎么办呢?其实memcpy它被设计出来就是为了拷贝不重叠的情况的,如果它拷贝到重叠的部分,结果都是未定义的。那我们就没办法完成拷贝重叠部分了嘛?别着急,下面这个函数就可以解决这个问题。

二、memmove——内存的移动魔法

memmove和memcpy几乎是一模一样的,唯一的差别就是它可以处理重叠的源内存块和目标内存块。

2.1 memmove的使用

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

来看刚刚的问题的代码解决:

#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;
}

运行结果:

它是怎么实现的呢?我们试着来模拟一下。

2.2 memmove的模拟实现

我们先来画图解析一下:

我们正常来讲是将1拷贝到3,2拷贝到4,是正着拷的。那如果我们反过来,先把5拷过去,倒着拷是不是就避免了被重叠覆盖的情况了。

这里有的人就耍小聪明了,就喜欢死记硬背了,正着拷不行,那就都倒着拷。

那再来看这张图:

如果我还是倒着拷的话,还是会覆盖。

所以我们得到了一个结论:当dest在src左面时,从前向后拷(正着拷);当dest在src右面时,从后向前拷(倒着拷)。

当你不理解的时候,画个图自己捋一下就好了。

思路有了,我们来看代码:

void* memmove(void* dst, const void* src, size_t count)
{void* ret = dst;if (dst <= src || (char*)dst >= ((char*)src + count)) {//正着拷形式如同memcpywhile (count--) {*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}}else {//倒着拷将首元素地址+字节数再-1即可指向最后一个字节。dst = (char*)dst + count - 1;src = (char*)src + count - 1;while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst - 1;src = (char*)src - 1;}}return(ret);
}

 三、memset——内存的初始化神器

从名字上来看,set译为设置,那这个函数的作用大概率是设置内存的。

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

首先来看它的三个参数:

1.ptr:一个void*类型的指针,指向要被填充的内存块。

2.value:要设置的值。

3.num:字节的个数(设置多少个value的值,以字节为单位)

也就是说memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。

 

它包含在头文件<string.h>中。

 因此memset经常被用来写游戏外挂。

我们来看代码实例:

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

运行结果:

切记:memset以字节为单位设置,若用于数组中,绝不是以元素为单位!!

 四、memcmp——内存的比较之术

经过这么多次的学习,我们已经对mem和cmp非常熟悉了,一个是内存,一个是比较。显然这个函数是用来比较两个内存块的大小的。

int memcmp(const void* ptr1, const void* ptr2, size_t num);
原理:⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节

返回值如下:

 其实和strcmp相类似。

代码示例:

#include <stdio.h>
#include <string.h>
int main()
{char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int n;n = memcmp(buffer1, buffer2, sizeof(buffer1));if (n > 0)printf("'%s' is greater than '%s'.\n", buffer1, buffer2);else if (n < 0)printf("'%s' is less than '%s'.\n", buffer1, buffer2);elseprintf("'%s' is the same as '%s'.\n", buffer1, buffer2);return 0;
}

来看运行结果:


 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何用ChatGPT写简历与自传How to Write a Resume and Autobiography with ChatGPT

在使用ChatGPT撰写简历和自传时&#xff0c;虽然它能够帮助您组织思路和提供结构建议&#xff0c;但请注意&#xff0c;由于ChatGPT并不了解您的个人经历、技能和具体工作细节&#xff0c;因此无法直接为您生成真实内容。以下是一个基本的指导框架&#xff0c;您可以根据这个框…

测试开发工程师(QA)职业到底需要干些什么?part5:性能测试工程师QA

工作职责 性能测试工程师&#xff08;Performance Testing Engineer&#xff09;是负责评估和优化软件、应用程序或系统在不同负载和压力条件下的性能的专业人员。他们的工作职责包括以下几个方面&#xff1a; 性能测试计划&#xff1a;性能测试工程师与开发团队、产品团队和系…

Vtk裁剪功能之平面裁剪vtkClipClosedSurface(vtk小记)

1.原理分析 对你的三维图形&#xff0c;使用一个平面切下去&#xff0c;然后保留一半。 确定一个平面&#xff1a;使用法向量和一个三维坐标点可以确定一个平面 原始图像 切一刀 切两刀&#xff0c;又一半 切三刀&#xff0c;又一半 源代码 #include <vtkActor.h> #i…

MySQL中如何进行多表查询

目录 一、子查询 1.什么是子查询 2.注意事项 二、联结查询 1.什么是联结 2.内部联结&#xff08;等值联结&#xff09; ①WHERE语句 ②ON语句 3.自联结 4.自然联结 5.外部联结 三、组合查询 1.什么是组合查询 2.UNION规则 *本节涉及概念来源于图灵程序设计丛书&a…

【jenkins+cmake+svn管理c++项目】Windows环境安装以及工具配置

一、目标和环境 目标&#xff1a;搭建一个jenkins环境&#xff0c;实现jenkins调用cmake和svn和VS编译c项目&#xff0c;并将生成的库上传svn。 环境&#xff1a;win10虚拟机&#xff08;练习流程用&#xff0c;正式用的话还是放到服务器&#xff09;&#xff0c;VS2017. 二、…

文件编辑命令—vim

1.vim vim 是vi的升级版本.vi 文件名(vi方向键用不了) vim 的官方网站 (welcome home : vim online) 自己也说 vim 是一个程序开发工具而不是文字处理软件。 2.安装vim sudo apt install vim 如果出错了:apt update:刷新软件源; 出现"无法获得锁 之类的"sudo rm 文件…

Linux 的 app :一般到哪里下载 ?(**)

利用 appimagetool 工具对开发好的项目进行打包 &#xff08;***带笔记*&#xff09; https://blog.csdn.net/ken2232/article/details/131313613 1. 首选&#xff0c;直接通过 OS发行版的官网仓库&#xff1a;简单、方便&#xff1b;可能相对最可靠。 如&#xff1a; sudo a…

如何用全身动作捕捉设备升级虚拟仿真实训室?

伴随着“科教强国”、“科教兴国”建设&#xff0c;数字化转型影响着职业教育未来&#xff0c;全身动作捕捉设备搭建数字人实训室&#xff0c;可以有效升级虚拟仿真实训室及相关建设&#xff0c;让实训教学体系更加完善。 以全身动作捕捉设备为主导的虚拟仿真实训室&#xff0…

RHCE-3-远程登录服务

简介 概念 远程连接服务器通过文字或图形接口方式来远程登录系统&#xff0c;让你在远程终端前登录linux主机以取得可操作主机接口&#xff08;shell&#xff09;&#xff0c;而登录后的操作感觉就像是坐在系统前面一样 功能: 分享主机的运算能力 服务器类型&#xff1a;有限…

0.96寸OLED屏调试 ----(三)

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、0.96寸OLED显示模块&#xff1b; 备注&#xff1a;专业版、升级版都适用&#xff1b; 经过前面两期的介绍&#xff0c;大家应该对OLED显示模块有了大致的了解&#xff0c;今天先来参插一下调试界面&…

第44期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

稀碎从零算法笔记Day27-LeetCode:螺旋矩阵

题型&#xff1a;矩阵(二维数组)、边界问题 链接&#xff1a;54. 螺旋矩阵 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 很有趣的…