C语言-------内存函数

前面向大家介绍了C语言中的字符函数和字符串函数,今天再向大家介绍一下类似的函数————

C语言中的内存函数。

1. memcpy函数的使用和模拟实现

memcoy函数是一种通过内存来复制内容的一种函数,以字节为基本单位进行,斌并且是一个可以复制任何类型数据的函数。

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

上面代码就是memcpy函数的一个基本形式。 

用法解释:destination是一个指向目标空间的 void* 类型的指针,source是一个指向源空间(提供复制的内容的对象)的指针。num是一个无符号整形的参数,单位是字节。

意思就是从source指向的位置开始向后复制num个字节的内容复制destination指向的位置。

使用时注意事项:

1. memcpy函数进行内容的复制时,遇到源空间的 '\0' 不会停止。

2. memcpy函数进行内容复制时的基本单位是字节

3. memcpy函数使用时,最好不要涉及到目标空间和源空间共同覆盖的空间,否则复制的结果都是未定义的。

老样子,上代码。

int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[5] = { 0 };printf("复制前arr2数组为:");int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr2[i]);}printf("\n");memcpy(arr2, arr1, 3 * sizeof(arr1[0]));//3*sizeof(arr1[0])是指将从arr1开始复制直到到达12个字节printf("复制后arr2数组为:");for (i = 0; i < 5; i++){printf("%d ", arr2[i]);}return 0;}

memcpy函数的模拟实现

void my_memcpy(void* dest, const void* src, int num) //设置成void* 型指针是为了接受任何类型的数据
{while (num--){*(char*)dest = *(char*)src;//因为void*指针无法直接进行解引用,我们将其转换成char*型的dest = (char*)dest + 1;src = (char*)src + 1;}
}
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[10] = { 0 };my_memcpy(arr2, arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;
}

以上就是memcpy函数的模拟实现

有人看了代码就疑惑了,为什么偏偏实现函数功能是将指针强制转换成char*的呢?

其实就是为了方便操作

本质上是将要复制的内容转换为一个一个字节复制过去,而char*型的指针一次就只能操作一个字节,就实现了通过一个一个字节来复制内容。 

运行如下图

实现模拟之后,我们再来解释一下为什么操作的空间最好不要有重叠。

void my_memcpy(void* dest, const void* src, int num) 
{while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10};my_memcpy(arr1+3, arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

 通过画图我们知道,按照上面代码操作,4和5的空间是共同覆盖的,所以当进行到复制半路的途中,4和5就会被改为1和2了,然而4和5也是后面要复制的内容,但是4和5已经提前被改为1和2了,所以后面的复制操作也是会有影响的。

运行代码

但是这种效果是通过我们模拟实现的。

如果我们真使用上memcpy函数时,memcpy函数还是会正常实现复制的。

这说明VS中memcpy函数已经具备了处理空间重叠的问题。但我们如果以后遇到的空间是重叠的,最好不要使用memcpy函数。

所以C语言就为我们提供了一个处理空间重叠的函数------memmove函数

2. memmove函数的使用和模拟实现 

其实memmove函数和memcpy函数的用法是差不多的,不同点在于memmove函数是专门设计来处理目标空间和源空间重叠的问题的。

需要注意的是:memmove函数处理的基本单位也是字节。

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

上代码

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

 

运行代码发现,memmove函数在处理目标空间和源空间重叠时,是可以正常将源空间的内容复制到目标空间的。

所以每当我们在复制内容时,遇到目标空间和源空间重叠的问题时,必须使用memmove函数。

memmove函数的模拟实现

在模拟实现memmove函数遇到的情况比较多也比较复杂。我们 一 一 来分析。

第一种情况

如上图,当dest和src指向的空间没有重叠时,我们只需要把src里面要复制的内容从前向后或者从后向前拷贝到目标空间中去。

第二种情况

如图,当src指向的空间于dest指向的空间有重叠部分时,并且src在dest的左边时,我们依次将src指向的内容从前向后拷贝到dest指向的空间时,我们会发现这样拷贝时,我们会将后面要拷贝的4和5变成了1和2,这样要拷贝4和5时就无法拷贝了,因为4和5应经提前被替换成1和2了,已经没有4和5了。

 所以这时候我们要改变一下,将src里面的内容从后向前拷贝。

如上图,当我们将src指向的内容依次从后向前拷贝到dest指向的目标空间时,我们会发现这样就能正常复制内容。

第3种情况

 

这时我们发现当空间有重叠时,并且src在dest的右边时,我们将src里面的内容依次从前向后拷贝的dest指向的空间时,是可以正常运行的。

总结: 

1.当 src在  1  区域时,也就是在dest的左边时,我们将src指向的内容从后向前拷贝。(src<dest)

2.当src在  2 和 3 区域时,也就是在dest的右边时,我们将src指向的内容从前向后拷贝。(src>dest)

代码实现

void my_memmove(void* dest, void* src, int num)
{if (src > dest){//从前向后拷贝while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src+1;}}else{while (num--){//num- -的同时,也样dest和src向后退了 *((char*)dest + num) = *((char*)src + num);}}
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr1) / sizeof(arr1[0]);my_memmove(arr1 + 2, arr1, 20);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr1[i]);}return 0;
}

运行代码

 

3.memset函数的使用

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

memset函数是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。

解释memset的3个参数

1.ptr 是一个void* 类型的指针,其指向的是被修改内容空间的起始位置。

2.value是我们要设置的内容。

3.num 是表示从ptr指向的起始位置开始向后有多少个字节的内容需要改变。

老样子,上代码感受

int main()
{char arr[] = "hallo bit";memset(arr, 'x', 5 * sizeof(arr[0]));printf("%s", arr);return 0;
}

 

4.memcmp函数的使用

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

memcmp函数是用来比较ptr1指向的内容和ptr2指向的内容后num个字节的内容的大小。

       返回值              *ptr1-*ptr2
          >0             >0
           0                ==0
            <0               <0

我们还是直接通过代码感受一下

int main()
{char* str1 = "abcd";char* str2 = "abce";int ret=memcmp(str1, str2, 4);printf("%d\n", ret);if (ret > 0){printf("str1>str2");}else if (ret < 0){printf("str1<str2");}else{printf("str1==str2");}
}

运行代码 

 

完结,感谢大家的观看。 

 

 

 

 

 

 

 

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

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

相关文章

git可视化工具

Gitkraken GitKraken 是一款专门用于管理和协作Git仓库的图形化界面工具。它拥有友好直观的界面&#xff0c;使得Git的操作变得更加简单易用&#xff0c;尤其适合那些不熟悉Git命令行的开发者。GitKraken提供了丰富的功能&#xff0c;如代码审查、分支管理、仓库克隆、提交、推…

如何使用CSS构建一个瀑布流布局

如何使用CSS构建一个瀑布流布局 瀑布流布局是一种常见的网页布局方式&#xff0c;其中元素以不同的大小排列&#xff0c;且行与列之间没有不均匀的间隙。在瀑布流布局中&#xff0c;即使某一行或列中的元素较短&#xff0c;下一个元素也会占据空间。 如何实现瀑布流布局 实现…

java框架学习——反射概述及简易版框架搭建

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! 反射 反射&#xff08;Reflection&#xff09;&#xff1a;加载类&#xff0c;并允许以编程的方式解剖类中的各种成分&#xff08;成员变量&#xff0c;方法&#xff0c;构造器等&#xff09; 1&…

C++之类和对象(上)

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 4.1访问限定符 4.2 类的两种定义方式 第一种&#xff1a; 第二种&#xff1a; 4.3封装 5.类的实例化 6.类对象模型 1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;…

出门一笑, “栈” 落江横 (Java篇)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

03-自媒体文章发布

自媒体文章发布 1)自媒体前后端搭建 1.1)后台搭建 ①&#xff1a;资料中找到heima-leadnews-wemedia.zip解压 拷贝到heima-leadnews-service工程下&#xff0c;并指定子模块 执行leadnews-wemedia.sql脚本 添加对应的nacos配置 spring:datasource:driver-class-name: com…

23年蓝桥杯省赛 动态规划DP

动态规划 就是:给定一个问题&#xff0c;我们把它拆成一个个子问题&#xff0c;直到子问可以直接解决。然后把子问题的答案保存起来&#xff0c;以减少重量计算&#xff0c;再根据子问题答察反推&#xff0c;得出问解的一种方法。 题目&#xff1a; 这天&#xff0c;一只蜗牛…

QT串口接收数据并进行波形显示(含源码)

**使用QT在串口调试助手基础上实现波形显示&#xff08;含源码&#xff09; 评论比较多留言需要源码的&#xff0c;逐个发邮箱比较麻烦也不能及时回复&#xff0c;现将源码上传至链接&#xff08;无需积分下载&#xff09;https://download.csdn.net/download/m0_51294753/877…

cJSON(API的详细使用教程)

我们今天来学习一般嵌入式的必备库&#xff0c;JSON库 1&#xff0c;json和cJSON 那什么是JSON什么是cJSON&#xff0c;他们之间有什么样的关联呢&#xff0c;让我们一起来探究一下吧。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&…

C和C++内存管理

目录&#xff1a; 一&#xff1a;C和C内存分布 二&#xff1a;C动态内存管理方式 三&#xff1a;C动态内存管理方式 四&#xff1a;operator new与operator delete函数 五&#xff1a;new和delete的实现原理 六&#xff1a;定位new表达式(placement-new) 七&#xff1…

使用YOLOv8训练自己的【目标检测】数据集

文章目录 1.收集数据集1.1 使用开源已标记数据集1.2 爬取网络图像1.3 自己拍摄数据集1.4 使用数据增强生成数据集1.5 使用算法合成图像 2.标注数据集2.1确认标注格式2.2 开始标注 3.划分数据集4.配置训练环境4.1获取代码4.2安装环境 5.训练模型5.1新建一个数据集yaml文件5.2预测…

了解IP地址的基本概念和修改步骤

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;其重要性不言而喻。无论是为了提升网络性能&#xff0c;还是出于隐私保护的需求&#xff0c;修改IP地址都是网络使用者可能遇到的操作。虎观代理将详细介绍如何修改IP地址&#xff0c;并探讨在修改过程中需要…

python+flask+django文献文件资料搜索系统

后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django/flask Python版本&#xff1a;python3.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm 本系统在设计过程中&#xff0c;很好地发挥了该开发方式的优…

例47:键盘事件演示

建立一个EXE工程&#xff0c;在默认窗体上放一个Image框和一一个text框。在text的按键事件中输入代码&#xff1a; Function Form1_Text1_WM_KeyDown(hWndForm As hWnd, hWndControl As hWnd,nVirtKey As Long, lKeyData As Long) As LongIf nVirtKey VK_SPACE ThenImage1.Pi…

Linux:IO多路转接之select

文章目录 selecttimeval结构体fd_set 优缺点分析完整代码 本节要介绍的主题是多路转接式IO select 先说结论&#xff0c;这个select是做什么的呢&#xff1f; select是负责在Linux系统中&#xff0c;让一个人可以有多个鱼竿&#xff0c;可以不停的进行轮询&#xff0c;只要有…

Trace链异常检测汇总

微服务应用与单块应用完全不同&#xff0c;一个微服务系统少则有几十个微服务组成&#xff0c;多则可能有上百个服务。比如BAT级别的互联网公司&#xff0c;一般都超过上百个服务&#xff0c;服务之间的依赖关系错综复杂&#xff0c;如果没有有效的监控手段&#xff0c;那么出现…

[计算机知识] 各种小问题思考

哈希算法以及哈希冲突 哈希算法&#xff1a;将任何长度的输入通过散列函数转换成固定长度的字符串 哈希冲突&#xff1a;不同的输入经过哈希函数处理后得到相同的哈希值 因为哈希函数的输出域是有限的 解决哈希冲突&#xff1a; 1. 开放寻址&#xff1a;产生哈希冲突后&…

Vue - 1( 13000 字 Vue 入门级教程)

一&#xff1a;Vue 导语 1.1 什么是 Vue Vue.js&#xff08;通常称为Vue&#xff09;是一款流行的开源JavaScript框架&#xff0c;用于构建用户界面。Vue由尤雨溪在2014年开发&#xff0c;是一个轻量级、灵活的框架&#xff0c;被广泛应用于构建单页面应用&#xff08;SPA&am…

创建和启动线程

概述 Java语言的JVM允许程序运行多个线程&#xff0c;使用java.lang.Thread类代表线程&#xff0c;所有的线程对象都必须是Thread类或其子类的实例。 Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的&#xff0c;因此把run()方法体称为线程执行体。…

使用神经网络-遗传算法优化神经网络-风电预测故障(BP,GABP,matlab)

本项目是故障预测&#xff0c;不是时序预测&#xff0c;本质还是分类问题 1 数据集介绍 特征文件&#xff1a; 标签文件&#xff1a;共计4个标签&#xff0c;其中大多数都是正常的&#xff0c;其他是3个不正常的类别 2 使用BP网络 2.1 读取数据&#xff0c;然后选择几个…