C语言内存函数

目录

  • memcpy(Copy block of memory)使用和模拟实现
    • memcpy的模拟实现
  • memmove(Move block of memory)使用和模拟实现
    • memmove的模拟实现:
  • memset(Fill block of memory)函数的使用
    • 扩展
  • memcmp(Compare two blocks of memory)函数的使用

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒个人主页
🥸🥸🥸C语言
🐿️🐿️🐿️C语言例题
🐣🐓🏀python

memcpy(Copy block of memory)使用和模拟实现

代码格式:

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

memcpy使用要点:
1:函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置

2:这个函数在遇到 ‘\0’ 的时候并不会停下来

3:如果source和destination有任何的重叠,复制的结果都是未定义的

4:由于不知道程序猿会传入什么样的指针,所以我们用void * 表示各种类型的指针,因此我们用void*后可以传入int * ,char * …类型的指针

此外我们还有区分 strcpy和memcpy,一个是拷贝字符,一个是拷贝内存,memcpy拷贝是针对不重叠的拷贝)
我们解释一下重叠的含义:

int main()
{int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };memcpy(arr+2, arr , 16);return 0;
}

这就是拷贝重叠,arr+2是表示的&a[2],arr是数组名表示首元素的地址,我们用一个图来说明
在这里插入图片描述

首先我们将a[0]=0,a[1]=1分别替换a[2]=2,a[3]=3,由于传进去的是地址,所以是永久改变,此时我们的a[2]和a[3]都发生了变化,但是a[2]和a[3]还没有去替换a[4]和a[5]

当我们用a[2]和a[3]去替换a[4]和a[5]时,a[2]和a[3]已经变成了0和1,所以替换a[4]和a[5]的结果也是0和1

只不过有些编译器的结果并不是这样的(比如VS),因为VS中的memcpy约等于memmove,唯一的解释就是程序猿在实现memcpy时的编写不同,所以代码的结果就不同,但是大多数编译器都是前面的结果

代码示例:

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

代码解析:
这里有两个int类型的数组,arr2中的元素全是0,我们将arr1中的元素复制到arr2中

由于我们传入的arr2和arr1都是数组的数组名,也就是数组首元素的地址,传入的字节数为20,因为一个数组中的元素大小为4个字节,所以相当于传入5个元素到arr2中

之后就从arr2中第一个元素0开始改变,总共改变5个元素,打印的结果如下
在这里插入图片描述

memcpy的模拟实现

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

代码解析:
因为是拷贝的内存,但是数组中的元素是整形元素,如果我们拷贝的内存大小不是4的倍数(一个整形大小为4个字节),比如7,那么可能就无法拷贝进去

因此我们需要将传入的数组元素地址进行强制类型转换成char,只有这样才能一个字节一个字节的拷贝, * (char)dest = * (char*)src是将dest和src强制转换为char*类型的指针,再解引用,将src的一个字节拷贝给dest**

而dest = (char)dest + 1和src = (char)src + 1都是将两个数组强制类型转换,再通过+1跳到下一个字节,这样我们就可以保证每一个字节都能够拷贝**

错误代码示例1:
void* my_memcpy(void* dest,const void* src, size_t sz)
{assert(dest && src);while (sz--){*(char*)dest = *(char*)src;(char*)dest++;(char*)src++;}return dest;
}
错误代码示例2:
void* my_memcpy(void* dest,const void* src, size_t sz)
{assert(dest && src);while (sz--){*(char*)dest = *(char*)src;dest = ++(char*)dest ;src = ++(char*)src ;}return dest;
}

错误1具体原因其实我也没有搞懂,错误2只能在某些编译器下可以正常运行,但不是全部,因此就算作错误

memmove(Move block of memory)使用和模拟实现

代码格式:

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

memmove使用要点:

1:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的

2:如果源空间和目标空间出现重叠,就得使用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, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

代码解析:
这里只有一个数组arr1,我们传入的地址为arr1+2和arr1,分别表示数组第三个元素的地址(&arr1[3]),和数组首元素的地址

在这里插入图片描述

memmove的模拟实现:

在实现之前我们先理解一下思路:
memmove的实现其实有一种比较简单的方法,就是我们只需要创造一个和arr一模一样的数组brr,由于是两个不同的数组,内存地址是不同的,只是数组元素是相同的,这样我们就解决重叠的问题了
在这里插入图片描述
但是我们能不能只通过arr自身而不去创造brr去实现memmove呢?
在这里插入图片描述
如图:我们需要将绿色方框中的元素拷贝到红色方框中的元素,可以像这样拷贝,我们先将a[4]=3拷贝到a[6]=5中,然后将a[3]=2拷贝到a[5]=4中…这样就可以避免重叠拷贝,造成拷贝元素不是我们想要的情况了

而如果我们需要将图中绿色方框中的元素拷贝到前面红色方框的元素呢?
在这里插入图片描述
其实方式都是一样的

综上如果我们只用数组arr而不创造数组brr的话,我们需要考虑到两种情况,一个是往前拷贝,一个是往后拷贝,所以针对情况我们就一定会用到if语句

具体实现代码如下:

void* my_memmove(void* dest, const void* src, size_t sz)
{assert(dest && src);void* ret = dest;//记录dest启始地址if (dest < src)//从前向后{for (int i = 0; i < sz; i++){*(char*)dest = *(char*)src;dest = (char*)+1;src = (char*)src + 1;}}else//从后向前{while (sz--){*((char*)dest + sz) = *((char*)src + sz);}}return ret;
}

代码解析:
* (char * )dest = * (char*)src是将dest和src强制转换为char*,方便后续可以一个字节一个字节的拷贝

dest = (char*)+1和src = (char*)src + 1是将两个数组都强转后向后移动一个字节

由于while(sz–)是先判断sz是否=0,判断完后再执行后置减减,然后在通过 *((char *)dest + sz) = * ((char * )src + sz)确定需要拷贝的数组和要拷贝数字的位置,我们用一个图来说明:

我们需要将绿色方框中的元素拷贝进红色方框中去,下面是各元素的16进制表示
在这里插入图片描述
在这里插入图片描述

memset(Fill block of memory)函数的使用

代码格式:

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

memset使用要点:
1:memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容

代码示例:

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

代码解析:
memset中我们传入的
在这里插入图片描述

扩展

memset是以字节为单位设置内存的,为了方便理解我们看一个代码

int main()
{int arr[10] = { 0 };memset(arr, 1, 40);for (int i = 0; i < 10; i++){printf("%d\n", arr[i]);
}return 0;
}

运行结果如图:
在这里插入图片描述
我们调试看一下内存
在这里插入图片描述
这是以16进制的方式存储的,在还没有执行memset时内存存储的每个字节都是0

在这里插入图片描述
而执行memset后我们可以看到每隔一个字节都会有一个0变成1,因此我们通过这个就比较容易理解以字节为单位设置内存这句话了

所以memset最好还是设置char类型的数组

memcmp(Compare two blocks of memory)函数的使用

代码格式:

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

memcmp使用要点:
1:比较从ptr1和ptr2指针指向的位置开始,向后的num个字节

2:memcmp是比较两个内存块

返回值如下
在这里插入图片描述
代码示例:

int main()
{int arr1[] = { 1,2,3,4,5,6,7 };//01 00 00 00  02 00 00 00  03 00 00 00 04 00 00 00  05 00 00 00...int arr2[] = { 1,2,3 };//01 00 00 00  02 00 00 00  03 00 00 00int a = memcmp(arr1, arr2, 12);printf("%d", a);return 0;
}

代码结果如下:
在这里插入图片描述
但如果我们稍微改一下代码呢?

int main()
{int arr1[] = { 1,2,3,4,5,6,7 };//01 00 00 00  02 00 00 00  03 00 00 00 04 00 00 00  05 00 00 00...int arr2[] = { 1,2,3,0x11223304 };//01 00 00 00  02 00 00 00  03 00 00 00  04 33 22 11int a = memcmp(arr1, arr2, 13);printf("%d", a);return 0;
}

可以看到我们将arr2中添加了一个16进制的元素0x11223304 ,并且在memcmp中我们将比较的12个字节改为13个字节,结果如下
在这里插入图片描述
结果是0,从这里我们也可以看出,memcmp也是将数组中的元素用16进制来表示,再通过一个字节一个字节的比较,方式有点像前面的memset

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

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

相关文章

DataX和dataX-web 集群部署及使用

&#x1f4d1; DataX和dataX-web 集群部署及使用 一 . 安装前准备 DataX 是一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。 DataX 采用 框架 插件 的模式…

[补题记录] Atcoder Beginner Contest 297(F)

URL&#xff1a;https://atcoder.jp/contests/abc297 目录 F Problem/题意 Thought/思路 Code/代码 F Problem/题意 给一个 H * W 的矩形&#xff0c;在其中任意放置 K 个点&#xff0c;由这 K 个点构成的最小矩形带来的贡献为该矩形的面积&#xff0c;这 K 个点构成一种…

网络和系统操作命令

目录 ping&#xff1a;用于检测网络是否通畅&#xff0c;以及网络时延情况。ipconfig&#xff1a;查看计算机的IP参数配置信息&#xff0c;如IP地址、默认网关、子网掩码等信息。netstat&#xff1a;显示协议统计信息和当前TCP/IP网络连接。tasklist&#xff1a;显示当前运行的…

[NISACTF 2022]popchains - 反序列化+伪协议

[NISACTF 2022]popchains 一、解题流程二、小小疑惑 一、解题流程 1、链条&#xff1a;Road_is_Long&#xff08;construct->wakeup【page$r】-> toString【string$m】&#xff09;-> Make_a_Change&#xff08;construct->get【effort$t】&#xff09;-> Try_W…

010:连续跌3天,同时这三天收盘价都在20日均线下,第四天上涨的概率--以京泉华为例

对于《连续跌三天&#xff0c;压第四天上涨的盈利计算》&#xff0c;我们可以继续优化这个策略&#xff0c;增加条件&#xff1a;同时三天都收盘在20日均线下。 因为我们上一篇《获取20日均线数据到excel表中》获得了20日均线数据&#xff0c;我们可以利用均线数据来编写新的脚…

OMV6 安装Extras 插件失败的解决方法

# Time: 2023/10/07 #Author: Xiaohong # 运行环境: OS: OMV6 # 功能: 安装Extras 插件失败的解决方法 问题描述&#xff1a;OMV6 安装插件omv-extras&#xff0c;只能按如下提示的命令行&#xff0c;但安装过程中&#xff0c;会提示raw.githubusercontent.com 无法访问插…

seata框架

Seata简介&#xff1a;&#xff08;Seata | Seata&#xff08;官方网站&#xff09;&#xff09; Seata 是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 相关术语&#xff1a; TC (Transaction Coordinator) - 事务…

2023年【煤炭生产经营单位(安全生产管理人员)】证考试及煤炭生产经营单位(安全生产管理人员)模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 煤炭生产经营单位&#xff08;安全生产管理人员&#xff09;证考试是安全生产模拟考试一点通生成的&#xff0c;煤炭生产经营单位&#xff08;安全生产管理人员&#xff09;证模拟考试题库是根据煤炭生产经营单位&…

Linux系统及Docker安装RabbitMq

目录 一、linux系统安装 1、上传文件 2、在线安装依赖环境 3、安装Erlang 4、安装RabbitMQ 5、开启管理界面及配置 6、启动 7、删除mq 二、docker安装 1、上传mq.tar包或使用命令拉取镜像 2、启动并运行 3、访问mq 一、linux系统安装 1、上传文件 2、在线安装依赖环…

我写过的最蠢的代码

目录 前言正文蠢代码 - 1蠢代码 - 2蠢代码 - 3提醒&#xff01; 蠢代码 - 4 总结 前言 每个人的身上都有毛毛 每个人的代码有些十分蠢&#xff0c;正巧&#xff0c;我也有&#xff01; 一看到CSDN的活动《你写过的最蠢的代码是&#xff1f;》&#xff0c;我立刻想要参加来告诉…

selenium +IntelliJ+firefox/chrome 环境全套搭配

1第一步&#xff1a;下载IntelliJ idea 代码编辑器 2第二步&#xff1a;下载浏览器Chrome 3第三步&#xff1a;下载JDK 4第四步&#xff1a;配置环境变量&#xff08;1JAVA_HOME 2 path&#xff09; 5第五步&#xff1a;下载Maven 6第六步&#xff1a;配置环境变量&#x…

搭建Windows上的Qt桌面开发环境

搭建Windows上的Qt桌面开发环境 准备有效邮箱安装VS2019 CommunityMicrosoft个人账号注册地址下载在线安装器安装C工具链 安装QtQt开发者账号注册地址下载在线安装器安装Qt 5.15工具链和Qt Creator 使用Qt Creator编译示例工程配置构建套件&#xff08;Kit&#xff09;打开示例…