C:内存函数

server/2024/9/24 5:43:56/

目录

前言:

一、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.ppmy.cn/server/121195.html

相关文章

STM32篇:开发环境安装

编程语言&#xff1a;C语言 需要安装的软件有两个&#xff1a;Keil5 和 STM32CubeMX 一.Keil5 的安装 使用 Keil4 写 STM32 代码其实也是可以&#xff0c;但需要很复杂的配置&#xff0c;不建议新手操作。 比较推荐 Keil5 编写 STM32 &#xff0c;只需要一些简单的设置就可…

关于支持向量机的一份介绍

在这篇文章中&#xff0c;我将介绍与支持向量机有关的东西&#xff0c;我们知道支持向量机主要分两类&#xff0c;就是线性支持向量机和核支持向量机这两种&#xff08;当然还有其他的&#xff0c;如多类支持向量机、 Nu-Support Vector Regression等&#xff09;&#xff0c;因…

李沐 模型选择、过拟合和欠拟合相关代码【动手学深度学习v2】

多项式回归 生成数据集 给定x,我们将使用以下三阶多项式来生成训练和测试数据的标签: y=5+1.2x−3.4+5.6+ϵ where ϵ∼( ). 噪声项ϵ服从均值为0且标准差为0.1的正态分布。 在优化

R18 NES 之SSB-less SCell operation for inter-band CA

在TR 21.918 Summary of Rel-18 Work Items 中可以看到SSB-less SCell operation for inter-band CA 是Network energy savings for NR 的一部分,其中还包括cell DTX/DRX 等等其他内容。 网络节能是 5G/NR 成功的关键,可以减少对环境的影响(温室气体排放)并节省运营成本。R…

后端常用的mybatis-plus方法以及配合querywapper使用

目录 一、插入数据 save方法 二、删除操作 removeById方法 三、更新操作 updateById方法 四、查询操作 selectById方法 五、条件构造器QueryWrapper的更多用法 1.比较操作符 2.逻辑操作符 3.模糊查询 4.空值判断 一、插入数据 save方法 save(T entity):向数据库中插入…

Java项目实战II基于Java+Spring Boot+MySQL的大型商场应急预案管理系统(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在快节奏的…

【Redis】Linux下安装配置及通过C++访问Redis

文章目录 一、Linux Centos 7.0版本下的安装及配置二、通过C访问Redis 一、Linux Centos 7.0版本下的安装及配置 通过源来安装&#xff0c;此次安装的版本为 redis 5.0 的&#xff0c;要通过其他源进行安装&#xff0c;首先安装 scl 源 yum install centos-release-scl-rh再安…

系统架构设计师:系统架构设计

简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 系统架构设计师:系统架构设计前言软件架构设计与生命周期SA 实现阶段主要关注的内容A…