C:内存函数

目录

前言:

一、memcpy 函数的使用及实现

1、memcpy函数的介绍

 1.1 memcpy函数参数解读

 2、memcpy函数的使用

3、memcpy函数的模拟实现

二、memmove函数的使用及模拟

1、memmove函数的使用

2、memmove函数的模拟实现

三、memset 函数的使用

1、memset函数的介绍(cplusplus)

2、memset函数的使用

2.1 memset函数对数组的应用

2.2 memset函数对字符串的应用

​编辑

 四、memcmp函数的使用

1、memcmp函数的介绍

2、memcmp函数的使用


前言:

上篇文章介绍了C语言字符串函数,我们学会了一些对字符串的操作,比如说将字符串从一个字符数组拷贝到另一个字符数组中,我们可以通过使用strcpy函数实现。但是,如果我们想要拷贝一个整型数组到另一个整型数组中时,strcpy函数就失效了,那我们应该怎么才能实现这个操作呢?不要着急,本篇文章将带大家搞定这个问题。

一、memcpy函数的使用及实现

1、memcpy函数的介绍

cplusplus上的介绍:

作用介绍:

参数介绍:

返回值介绍:

 1.1 memcpy函数参数解读

(1)void* destination

该参数的作用是目标空间,用来存放将要拷贝的内存,为什么返回值是 void* 呢?这是因为这个函数的作用是内存拷贝,既然是内存拷贝,内存中又可能存放的是整型数组,也有可能存放的是字符数组……,因此我们不关心存放数据的类型,因此使用void*指针来接收任意类型的数据的地址。

(2)const void* source

source是源头,也就是要拷贝的内存数据,这里也是void*指针是因为我们不知道我们未来要拷贝的数据是什么类型的,可能是整型,可能是字符,也可能是结构体,因此我们也使用void*。

用const修饰是因为我们不希望要拷贝的数据被修改,因此使用const修饰会使得整个工程更加稳定。

(3)size_t num

num的作用是限定拷贝的字节数,比如说source中有十个字节的数据,我们可以通过修改num的值来拷贝我们想要的个数,num为5,我们就拷贝五个字节的数据到destination中;num的类型是size_t的原因是我们拷贝的个数最低都是0个,不会出现负数的情况,因此使用size_t类型最为合适。

 2、memcpy函数的使用

前面学习了memcpy函数,接下来我们将使用memcpy函数来实现一些操作

比如说我们打算将整型数组arr1[ ] = {1,2,3,4,5,6,7,8,9,10}拷贝到整型数组 arr2[10] = { 0 };

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 5 * sizeof(int));return 0;
}

调试结果:

源头从 3 开始拷贝,比如说:

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1+2, 5 * sizeof(int));return 0;
}

 一些总结:

  • 函数memcpy从source的位置开始向后赋值num个字节的数据到destination指向的内容;
  • 这个函数在遇到\0的时候并不会停下来,与字符串函数不同;
  • 如果source和destination有任何的重叠,赋值的结果都是未定义的。

3、memcpy函数的模拟实现

接下来我们尝试自己写一个函数来实现memcpy的功能

void* my_memcpy(void* dest, const void* src, size_t num)
{}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };my_memcpy(arr2, arr1, 5 * sizeof(int));return 0;
}

注意:在my_memcpy中,源头是void*指针类型的  

现在有一个问题,我们不知道我们要拷贝的是内容是什么类型的,我们只知道要拷贝的是20个字节,我们该怎么将这20个字节拿到arr2中呢?

在前面学习qsort函数的模拟实现中,我们用到了一个方法,我们可以一个字节一个字节的拷贝,那么就可以使用强制类型转换将void*指针转换为char*指针

void* my_memcpy(void* dest, const void* src, size_t num)
{while (num--)//num是字节总数,因此num每减1,就拷贝一个字节{*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}
}

调试结果: 

总代码:

#include <stdio.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{// 保存目标地址,以便最后返回void* ret = dest;// 循环 num 次进行逐个字节的复制while (num--){// 将源地址指向的内容复制到目标地址指向的位置,并转换为 char* 类型进行操作,确保每次只复制一个字节*(char*)dest = *(char*)src;// 目标地址向后移动一个字节dest = (char*)dest + 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(arr2, arr1, 5 * sizeof(int));return 0;
}

如果我们向将int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };中(1,2,3,4,5)拷贝放到(3,4,5,6,7)的位置上,这样可以实现吗?

	my_memcpy(arr1+2, arr1, 5 * sizeof(int));

调试结果:

为什么这里会是(1,2,1,2,1)呢?其实也很好理解

 因此最终结果是(1,2,1,2,1,2,1,8,9,10) 

关于重叠问题,我们一般使用后面的这个函数memmove函数

而memcpy函数一般用来处理不重叠情况。

在vs2022中,memcpy的能力是比较强的,也是可以用来处理重叠问题,但是对于memcpy函数,本来的作用是不包括处理重叠的问题的,这就像是老师说让你考到60分就行,但是你能考100分。但是不能保证在所有的编译器上memcpy都可以考到100分

也就是说无法确定其它编程环境是否可以实现,因此,如果要处理重叠问题,最好还是交给memmove函数.

二、memmove函数的使用及模拟

1、memmove函数的使用

memove函数的使用与memcpy函数是一样的,也是用来实现内存中数据的拷贝的,因此就不详细介绍了。不过前面也说了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, 5 * sizeof(int));return 0;
}

调试结果:
 可以看到的确将(1,2,3,4,5)的内容拷贝到(3,4,5,6,7)的位置上了

那么memmove函数究竟是怎么实现这个操作的呢?我们来模拟了解一下

2、memmove函数的模拟实现

前面我们知道,如果拷贝1,会把3给覆盖,拷贝2,会把4给覆盖。

该怎么拷贝才能实现不被覆盖呢?

可以从后向前拷贝,先拷贝5,覆盖7,在拷贝4覆盖6,这时候在拷贝3覆盖5,拷贝2覆盖4,拷贝1覆盖3,由于3,4,5已经拷贝完成,不会出现还没有拷贝就被覆盖的情况。

那是不是从后向前拷贝就一定正确呢?

我们在换一种情况试试:

这时候如果还是从后向前拷贝的话会出现什么问题呢?

8拷贝到6,7拷贝到5,这时候向拷贝6的时候已经变成了8,因此从后向前失效了。

这时候我们在从前向后拷贝,3拷贝到1,4拷贝到2……恰好可以全部拷贝。

不知道大家有没有发现一个规律:

如果dest在src的后面,则从后向前拷贝;

如果dest在src的前面,则从前向后拷贝;

如果没有重叠,则随意。

如果是 后->前,该怎么拷贝呢?

比如说先拷贝5,我们只需要在起始位置跳过num个字节即可

比如说:*((char*)src + num)

代码实现:

#include <stdio.h>
#include <string.h>
void* my_memmove(void* dest, const void* src, size_t num)
{void* ret = dest;if (dest < src){//前->后while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{while (num--){//后->前//num进来减1,变为19,src加上19后跳到最后一位上,也就是5,dest加上19跳到8的位置,然后将5赋值到8的位置*((char*)dest + num) = *((char*)src + num);}}return ret;
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr+2, arr, 5 * sizeof(int));return 0;
}

三、memset函数的使用

1、memset函数的介绍(cplusplus)

参数介绍:
注意:

memset是以字节为单位来设置内存的 ,而不是以一个元素为单位设置的。

 作用介绍:

返回值介绍:

2、memset函数的使用

2.1 memset函数对数组的应用

那么memset函数究竟有什么作用呢?

比如说:

#include <stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){arr[i] = i + 1;}return 0;
}

我们想将arr数组全部初始化为0,我们该怎么做呢?

你可能会说这不简单?直接使用循环不就可以了吗?

for (i = 0; i < 10; i++)
{arr[i] = 0;
}

这样的确可以,不过我们也可以使用库函数memset函数来实现这个操作。

我们要设置的这个空间整型数组arr[10]的地址交给ptr,而数组的地址就是数组名arr,我们需要将该数组的元素都变为0,也就是要设置的值value为0,由于是整型数组,有十个元素,所以num就等于40字节。

代码展示:

#include <stdio.h>
#include <string.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){arr[i] = i + 1;}/*for (i = 0; i < 10; i++){arr[i] = 0;}*/memset(arr, 0, 10 * sizeof(int));for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

调试过程:

结果展示:

 如果memset(arr, 1, 10 * sizeof(int));这是否是将每一个元素都改为1了呢?

调试监控窗口

为什么没有达到想要的结果呢?

我们在来看一下内存窗口:

破案了,memset函数将每一个字节都设置为1,而不是把一个元素设置为1。

前面强调了memset是以字节为单位来设置内存的 ,而不是以一个元素为单位设置的。

2.2 memset函数对字符串的应用

代码:

#include <stdio.h>
#include <string.h>
int main()
{char arr[] = "hello world";//如何将helo改为五个xmemset(arr, 'x', 5);return 0;
}

调试结果:

如果我们想修改world呢?

memset(arr+6, 'x', 5);

从前向后数hello五个字符,还有一个空格,共6个字符。

注意:这个函数比较常见,因此需要熟练掌握!!! 

 四、memcmp函数的使用

memcmp函数与之前学习的strcmp函数的功能是比较相似的,不过strcmp函数只能用来做字符串的比较,而memcmp函数是用来做内存块的比较,不论是什么类型。

1、memcmp函数的介绍

参数介绍:
 

返回值介绍:

2、memcmp函数的使用

直接上例题,比较arr1与arr2中前3个元素

#include <stdio.h>
#include <string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[] = { 1,2,3,3 };int ret = memcmp(arr1, arr2, 12);printf("%d\n", ret);return 0;
}

结果:

如果我们比较前4个元素呢?

int ret = memcmp(arr1, arr2, 16);、

这里返回的就是1了。


结语:本篇文章到这里就结束啦!期待下次的相遇!!

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

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

相关文章

PyCharm下载和安装教程

Python、C/C、C#、DSL、Go、Groovy、Java、JavaScript、Objective-C、PHP 等编程语言。 图 1 JetBrains 开发工具 PyCharm下载和安装 进入 PyCharm官方下载页面(如图 2 所示)&#xff0c;可以看到 PyCharm 有 2 个版本&#xff0c;分别是 Professional(专业版)和 Community(社…

Mybatis百万数据插入(含导出)

1 一般一次性插入多条数据 传统的sql语句&#xff1a; INSERT INTO table1 ( field1, field2 ) VALUES( "data1", "data2" ); INSERT INTO table1 ( field1, field2 ) VALUES( "data1", "data2" ); INSERT INTO table1 ( field1, fi…

DirectX修复助手

在日常使用电脑时&#xff0c;我们可能会遇到提示缺少DLL文件&#xff0c;如0xc000007b错误、缺少d3dxxx.dll等问题&#xff0c;这些会影响软件运行甚至导致系统不稳定。以下是一些常见的DLL问题原因和一个修复工具&#xff0c;希望能帮到你。 DLL文件问题的常见原因 软件安装…

20 基于STM32的温度、电流、电压检测proteus仿真系统(OLED、DHT11、继电器、电机)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STM32F103C8T6 采用DHT11读取温度、滑动变阻器模拟读取电流、电压。 通过OLED屏幕显示&#xff0c;设置电流阈值为80&#xff0c;电流小阈值为50&#xff0c;电压阈值为60&#xff0c;温度阈值…

24. Revit API: 几何对象(五)- (Sur)Face

一、前言 虽然Face是GeometryObject的子类&#xff0c;Surface不是&#xff0c;但这两者之间还是挺有关联的&#xff0c;每个Face都有一个对应的Surface&#xff0c;类似于Edge和Curve的关系。 Surface是数学意义上的面&#xff0c;纯定义。 Face是几何形状&#xff08;实体&a…

css如何设置间距

在CSS中设置间距是非常常见的需求&#xff0c;可以通过多种属性来实现。以下是一些常用的CSS属性及其用法&#xff0c;用于设置元素之间的间距&#xff1a; 内边距&#xff08;Padding&#xff09; padding 属性用于设置元素内容与元素边框之间的距离。可以分别设置四个方向的…

视频质量评价SimpleVQA

目录 一、研究意义 例子 二、介绍 三、文章解读 3.1 论文动机 3.2论文思路 3.3方法 3.3.1网络框架 3.3.2公式解读 3.3.3核心创新 3.3.4理解 &#xff01;&#xff01;&#xff01;作者对模型的改进 本人算法框体 3.3.5实验细节&#xff1a; 四、代码复现 4.1代码文件简介 4.2数…

leetcode第二十六题:删去有序数组的重复项

给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你…

C++之STL—vector容器基础篇

头文件 #include <vector> //vector容器 #include <algorithm> //算法 基本用法&&概念 vector<int> v; v.push_back(10); vector<int >::iterator v.begin(); v.end(); 三种遍历方式 #include <vector> #include <algorithm>…

Leetcode3289. 数字小镇中的捣蛋鬼

Every day a Leetcode 题目来源&#xff1a;3289. 数字小镇中的捣蛋鬼 解法1&#xff1a;哈希 代码&#xff1a; /** lc appleetcode.cn id3289 langcpp** [3289] 数字小镇中的捣蛋鬼*/// lc codestart class Solution { public:vector<int> getSneakyNumbers(vector…

在线文档搜索服务测试报告

目录 1. 项目背景: 2. 项目功能: 3. 测试计划: 1. 项目背景: 1.1 在线搜索服务的前端主要一下几个功能, 分别是进入搜索引擎界面(有提示输入关键词信息); 进行输入关键词的界面, 以及显示有关关键词的文档url, 点击跳转至目标文档的界面; 1.2 该在线搜索服务的文档可以实现用…

精彩回顾|博睿数据Bonree ONE 3.0产品发布会圆满落幕:三城联动 共襄盛举!

在金秋九月的璀璨时刻&#xff0c;博睿数据于9月20日在北京圆满举办了Bonree ONE 3.0产品发布会的收官之站。此前&#xff0c;这一盛会已在上海、广州相继绽放光彩&#xff0c;三城联动&#xff0c;共襄盛举&#xff0c;不仅展现了博睿数据在可观测性领域的深厚积淀与前瞻视野&…

一行命令,一分钟轻松搞定SSL证书自动续期

httpsok 是一个便捷的 HTTPS 证书自动续签工具&#xff0c;专为 Nginx 服务器设计。已服务众多中小企业&#xff0c;稳定、安全、可靠。现在的网站SSL免费证书有效期只有3个月&#xff0c;所以就会有经常更快SSL证书的需求&#xff0c;如果手上需要更换的SSL证书比较多的情况下…

leetcode第80题:删除有序数组的重复项(||)

给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 说明&…

【第十一章:Sentosa_DSML社区版-机器学习之分类】

目录 11.1 逻辑回归分类 11.2 决策树分类 11.3 梯度提升决策树分类 11.4 XGBoost分类 11.5 随机森林分类 11.6 朴素贝叶斯分类 11.7 支持向量机分类 11.8 多层感知机分类 11.9 LightGBM分类 11.10 因子分解机分类 11.11 AdaBoost分类 11.12 KNN分类 【第十一章&…

【毕业论文+源码】基于ASP的课程指导平台的开发

引 言 随着全球信息化技术的兴起&#xff0c;特别是Internet的日益普及&#xff0c;解决了信息Internet上传递的问题&#xff0c;建立了一个组织得很好的信息结构框架&#xff0c;使得Internet用户能够在Internet上的任何一个终端&#xff0c;以一种简单、统一的方式来访问超…

软考中级系统集成项目管理证书好考吗

系统集成项目管理工程师证书是中国计算机技术职业资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;中的中级资格之一。该证书是由人社部和工信部共同颁发&#xff0c;且证书上有这两个国家部门的印章&#xff0c;具有较高的职业认可度和市场价值。 系统…

调用JS惰性函数问题

第一次调用这个函数时 console.log(a) 会被执行&#xff0c;打印出 a&#xff0c;全局变量 a 被重定义并被赋予了新的函数&#xff0c;当再一次调用时&#xff0c;console.log(b) 被执行。 用处&#xff1a;因为各浏览器之间的行为差异&#xff0c;经常会在函数中包含了大量的…

从决策树到GBDT、随机森林

何为决策树 决策树&#xff08;Decision Tree&#xff09;&#xff0c;它是一种以树形数据结构来展示决策规则和分类结果的模型&#xff0c;作为一种归纳学习算法&#xff0c;其重点是将看似无序、杂乱的已知数据&#xff0c;通过某种技术手段将它们转化成可以预测未知数据的树…

以题为例浅谈反序列化漏洞

什么是反序列化漏洞 反序列化漏洞是基于序列化和反序列化的操作&#xff0c;在反序列化——unserialize()时存在用户可控参数&#xff0c;而反序列化会自动调用一些魔术方法&#xff0c;如果魔术方法内存在一些敏感操作例如eval()函数&#xff0c;而且参数是通过反序列化产生的…