【C语言】内存函数的概念,使用及模拟实现

Tiny Spark get dazzling some day.

目录

  • 1. memcpy
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 2.memmove
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 3. memset
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
  • 4. memcmp
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现

1. memcpy

  • 使用需包含头文件:<string.h>

– 函数原型

#include <string.h> // 头文件
void* memcpy ( void* destination, const void* source, size_t num ); 目标空间               源内容         拷贝数目

memcpy 函数的作用,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。

规则:

  1. 源指针目标指针 指向的对象的 基础类型 与此函数无关,即: memcpy函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
  2. memcpy 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
  3. 为避免溢出,两个参数 所指向的空间最好不是同一个(否则可能会造成重叠问题,这是更推荐使用的是 memmove 函数)。
  4. 确保 目标空间 有足够位置存放要复制的内容。

这哥们有点和 字符串函数的 strcpy 函数类似,都是把一段内容复制粘贴到另一个空间。不同的是 strcpy 只能复制 字符串内容, 而 memcpy 能复制任意类型的内容。


– 函数使用

int dest_arr[10]; 
int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
int sz = sizeof(src_arr) / sizeof(src_arr[0]);
memcpy(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9

memcpy 并没有到 ’ \0 '停止了而是继续复制,我理解为函数并没有去检测 ’ \0 ’ 的含义,而是仅仅将其当作数组元素一个数据而已。

我们再来看看第二种—复制字符串:

char dset_str[20];
char src_str[] = "Jackie\0Chan";
int sz = sizeof(src_str) / sizeof(src_str[0]);
memcpy(dest_str, src_str, sz * sizeof(char);// 把整个源数组的内容都复制过去
//此时 dest_arr[10] 里面的内容就是 Jackie\0Chan
printf("%s", dest_str);
// 但是,在 printf 打印的时候不要误跟着以为 也会不检测'\0'而打印 "Jackie\0Chan"
// 从而可能一脸懵逼 (me),或者 "这不常识?~不屑" (大佬you)

第三种–如果 源空间目标空间 重叠时使用情况:

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
memcpy(arr + 2, arr, 8 * sizeof(int));// 源空间开头为 第三个元素的位置 
for(int i= 0; i < 10; i++)printf("%d ",  arr[i]);

在VS2022上可能直接用可能会正常打印 1 2 1 2 3 4 5 6 7 8
但如果用 模拟函数实现的方法 执行程序:
在这里插入图片描述

这种是错误的
下面来解释下:
在这里插入图片描述
所以,若接着往下走,最后的输出结果就是

1 2 1 2 1 2 1 2 1 2

所以在使用 memcpy 函数时尽量不要 将 目标指针源指针 指向同一块空间。


– 函数的模拟实现

#include <assert.h>
void* Sim_memcpy(void* dest, const void* src, size_t num)
{void* ret = dest;assert(dest);assert(src);//先检测 dest 和 src 是否为空指针while (num--) {*(char*)dest = *(char*)src;// 非常精细,一个字节一个字节地复制dest = (char*)dest + 1;src = (char*)src + 1;}return(ret);
}

2.memmove

  • 使用需包含头文件:<string.h>

– 函数原型

void* memmove ( void* destination, const void* source, size_t num );目标空间				  源内容         拷贝数目

memmove 函数的作用 和 memcpy 基本相同,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。
但是, memcpy 不能作用在 同一块空间, 也就是无法在空间重叠下复制,而 memmove 可以解决这个问题。

规则:

  1. 源指针目标指针 指向的对象的 基础类型 与此函数无关,即: memmove函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
  2. memmove 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
  3. memmove在拷贝时会有一个 缓冲区 来接受源空间,所以允许 源空间目标空间 重叠。
    4。 确保 目标空间 有足够的位置来存放复制的内容。

– 函数使用

第一种使用情况和 memmove 基本相同

int dest_arr[10]; 
int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
int sz = sizeof(src_arr) / sizeof(src_arr[0]);
memmove(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9

第二种—如果 源空间 和 目标空间 重叠时使用情况:

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
memmove(arr + 2, arr, 8 * sizeof(arr));
for(int i= 0; i < 10; i++)printf("%d ",  arr[i]);

结果是:

1 2 1 2 3 4 5 6 7 8

那为什么 memcpy 无法满足实现空间重叠的情况,而 memmove 却可以呢?
在这里插入图片描述
所以,在往后遇到 目标指针源指针 指向的空间发生空间重叠时,可以使用 memmove 函数来解决。

– 函数的模拟实现

#include <assert.h>
void* Sim_memmove(void* dest, const void* src, size_t num)
{void* ret = dest;assert(dest);assert(src);//先检测 dest 和 src 是否为空指针if (dest <= src || (char*)dest >= ((char*)src + num)) // 从前往后拷{while (num--) {*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else // 从后往前拷{dest = (char*)dest + count - 1;src = (char*)src + count - 1;while (numt--) {*(char*)dest = *(char*)src;dest = (char*)dest - 1;src = (char*)src - 1;}}return(ret);
}

有点小难理解,下面我们来看看:

首先定义一个数组:int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

第一种情况:

Sim_memmove(arr + 2, arr, 5 * sizeof(int));

`在这里插入图片描述
第二种情况:

Sim_memmove(arr, arr + 2, 5 * sizeof(int));

在这里插入图片描述
所以在模拟实现 memmove函数时,要确定拷贝顺序是 从前往后 还是 从后往前
在这里插入图片描述


3. memset

  • 使用需包含头文件:<string.h>

– 函数原型

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

memset 函数用来将 ptr 指向的内存块的的指定范围(num个 字节)设置为指定值。

– 函数使用

char str[] = "Hello World";
memset(str, 'X', 5);
printf("%s", str);

输出结果:

XXXXX World

– 函数的模拟实现

#include <assert.h>
void* Sim_memset(void* ptr, int value, size_t num)
{void* ret = ptr;assert(ptr != NULL);// 先检测 ptr 是否为空指针while (num--){*(char*)ptr = value;(char*)ptr += 1;}return ret;
}

4. memcmp

  • 使用需包含头文件:<string.h>

– 函数原型

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

memcmp 函数用来比较 ptr1 指向的内存块前 num个字节 的内容和 **ptr2 ** 指向的内存块的前 num个字节 的内容。

规则:

  1. 该函数和 strcmp 相似,都是比较函数,但是 memcmp 函数在找到 ’ \0 ’ 字符后不会停止比较。
  2. 该函数会返回一个整形值,该值指示的内存块的内容之间关系如下:
返回值表明
<0在两个内存块中不匹配的第一个字节在 ptr1 中的值低于 ptr2 中的值
=0两个内存块的内容相等
>0在两个内存块中不匹配的第一个字节在 ptr1 中的值大于 ptr2 中的值

– 函数使用

比较两个数组

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,11 };
int ret = memcmp(arr1, arr2, 10 * sizeof(int));
if (ret < 0)printf("arr1 < arr2");
else if (ret == 0)printf("arr1 == arr2");
elseprintf("arr1 > arr2");

输出结果:

arr1 < arr2

比较两个字符串

char* str1 = "ABCDE";
char* str2 = "ABCDW";
int ret = memcmp(str1, str2, 10 * sizeof(char));
if (ret < 0)printf("arr1 < arr2");
else if (ret == 0)printf("arr1 == arr2");
elseprintf("arr1 > arr2");

输出结果

arr1 < arr2

– 函数的模拟实现

int Sim_memcmp(const void* ptr1, const void* ptr2, size_t num)
{assert(ptr1 != NULL);assert(ptr2 != NULL);// 先判断 ptr1 和 ptr2 是否为空指针while ((*(char*)ptr1 == *(char*)ptr2) && num){((char*)ptr1)++;((char*)ptr2)++;}return *(char*)ptr1 - *(char*)ptr2;// 返回该字节内容的对应的 ASCII码表值的 差值
}

该函数的模拟实现和 strcmp 的函数模拟实现有点相似,不同的是,strcmp 需要实现遇到
’ \0 ’ 时便停止比较,而 memcmp 则是直接忽略 ’ \0 ’ 继续比较(字符若为 ’ \0 ’ 也会执行一次比较) 。

  
  
  Stay hungry. Stay Foolish. 饥渴求知,虚怀若愚。
  感谢各位读者支持,虚心请教,如有错漏或可改进点,请任意指出,感激不尽!
  一起进步!


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

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

相关文章

BI赋能金融新质生产力,16家金融机构智能BI创新实践分享

2024年政府工作报告强调&#xff0c;要“大力发展科技金融、绿色金融、普惠金融、养老金融、数字金融”&#xff0c;同时“大力推进现代化产业体系建设&#xff0c;加快发展新质生产力”。对于金融行业而言&#xff0c;培育新质生产力是高质量发展的关键着力点。金融机构可以通…

【JavaWeb】Servlet+JSP+EL表达式+JSTL标签库+Filter过滤器+Listener监听器

需要提前准备了哪些技术&#xff0c;接下来的课才能听懂&#xff1f; JavaSE&#xff08;Java语言的标准版&#xff0c;Java提供的最基本的类库&#xff09; Java的开发环境搭建Java的基础语法Java的面向对象数组常用类异常集合多线程IO流反射机制注解Annotation… MySQL&…

FinalShell连接虚拟机Linux系统连接超时

报错信息 java.net.ConnectException: Connection timed out: connect 排除是网络问题后可以尝试一下这个方法。 解决方案: 打开虚拟机终端输入:ifconfig 会出现端口信息: 看ens33这里的端口是多少&#xff0c;改一下重新连接就ok。

保研面试408复习 4——操作系统、计网

文章目录 1、操作系统一、文件系统中文件是如何组织的&#xff1f;二、文件的整体概述三、UNIX外存空闲空间管理 2、计算机网络一、CSMA/CD 协议&#xff08;数据链路层协议&#xff09;二、以太网MAC帧MTU 标记文字记忆&#xff0c;加粗文字注意&#xff0c;普通文字理解。 1、…

系统运维(虚拟化)

1.VLAN VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。 每个VLAN是一个广播域&#xff0c;VLAN内的主机间可以直接通信&#xff0c;而VLAN间则不能直接互通。这样&#xff0c;广播报…

C++ | Leetcode C++题解之第61题旋转链表

题目&#xff1a; 题解&#xff1a; class Solution { public:ListNode* rotateRight(ListNode* head, int k) {if (k 0 || head nullptr || head->next nullptr) {return head;}int n 1;ListNode* iter head;while (iter->next ! nullptr) {iter iter->next;n…

Spring Boot 整合 socket 实现简单聊天

来看一下实现的界面效果 pom.xml的maven依赖 <!-- 引入 socket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- 引入 Fastjson &#x…

ifconfig命令找不到 command not found

问题 今天解决虚拟机的网络问题后&#xff0c;使用ifconfig发现报错命令未找到 解决方案 输入yum install ifconfi的程序安装包 yum install ifconfig 如果显示没有可用软件包 ifconfig&#xff0c;错误&#xff1a;。 就输入yum search ifconfig匹配安装包程序 yum searc…

【有趣的透镜】1.透镜初相识

1.透镜的外形和材料 (1)透镜由玻璃或者塑料制成&#xff1b; (2)透镜一般为圆型&#xff0c;其单面或双面为球面&#xff1b; 2.透镜的类型和折射 (1)球面外凸为凸透镜(聚光)&#xff0c;球面内凹为凹透镜(散光)&#xff1b; (2)透镜是基于光的折射&#xff0c;只要光从一…

ChatPPT开启高效办公新时代,AI赋能PPT创作

目录 一、前言二、ChatPPT的几种用法1、通过在线生成2、通过插件生成演讲者模式最终成品遇到问题改进建议 三、ChatPPT其他功能 一、前言 想想以前啊&#xff0c;为了做个PPT&#xff0c;我得去网上找各种模板&#xff0c;有时候还得在某宝上花钱买。结果一做PPT&#xff0c;经…

双层嵌线和线径的替代方案

电机只有三种嵌线方式 1.单层嵌线 2.双层嵌线 3.单双层嵌线 前面说的都是单层嵌线&#xff0c;下面介绍双层嵌线&#xff01; 双层嵌线一般线径都比较粗&#xff01; 线径只有几种规格的&#xff0c;大线径可用几根小线径替代&#xff01; 满足的原则&#xff1a;大线径A的…

【华为】路由综合实验(OSPF+BGP基础)

【华为】路由综合实验 实验需求拓扑配置AR1AR2AR3AR4AR5PC1PC2 查看通信OSPF邻居OSPF路由表 BGPBGP邻居BGP 路由表 配置文档 实验需求 ① 自行规划IP地址 ② 在区域1里面 启用OSPF ③ 在区域1和区域2 启用BGP&#xff0c;使AR4和AR3成为eBGP&#xff0c;AR4和AR5成为iBGP对等体…

2024面试自动化测试面试题【含答案】

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

C/C++ BM30 二叉搜索树与双向链表

文章目录 前言题目解决方案一1.1 思路阐述1.2 源码 解决方案二2.1 思路阐述2.2 源码 总结 前言 这道题要明白二叉搜索树的概念&#xff0c;同时还要对链表的知识比较熟悉。 题目 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 数据范…

网页主题自动适配:网页跟随系统自动切换主题

主题切换是网站设计中一个非常有趣的功能&#xff0c;它允许用户在多种预先设计的样式之间轻松切换&#xff0c;以改变网站的视觉表现。最常见的就是白天和黑夜主题的切换&#xff0c;用户可以根据自己的喜好进行设置。 除了让用户手动去切换主题外&#xff0c;如果能够让用户第…

TypeScript学习日志-第十九天(namespace命名空间)

namespace命名空间 一、基本用法 namespace 所有的变量以及方法必须要导出才能访问&#xff0c;如图&#xff1a; 二、 嵌套 namespace 可以进行嵌套使用&#xff0c;如图&#xff1a; 它也必须需要导出才能访问 三、合并 当我们出现两个同名的 namespace 它就会合并这两…

docker Harbor私有仓库部署管理

搭建本地私有仓库&#xff0c;但是本地私有仓库的管理和使用比较麻烦&#xff0c;这个原生的私有仓库并不好用&#xff0c;所以我们采用harbor私有仓库&#xff0c;也叫私服&#xff0c;更加人性化。 一、什么是Harbor Harbor是VWware 公司开源的企业级Docker Registry项…

【SpringBoot记录】自动配置原理(1):依赖管理

前言 我们都知道SpringBoot能快速创建Spring应用&#xff0c;其核心优势就在于自动配置功能&#xff0c;它通过一系列的约定和内置的配置来减少开发者手动配置的工作。下面通过最简单的案例分析SpringBoot的功能特性&#xff0c;了解自动配置原理。 SpringBoot简单案例 根据S…

百面算法工程师 | 支持向量机面试相关问题——SVM

本文给大家带来的百面算法工程师是深度学习支持向量机的面试总结&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们还将介绍一些常见的深度学习算法工程师面试问题&#xff0c;并提供参考的回答…

python代码无法点击进入,如何破???

python代码无法点击进入&#xff0c;如何破&#xff1f;&#xff1f;&#xff1f; 举个栗子&#xff1a; model.chat是无法进入的&#xff0c;这时可以使用如下的命令进行操作&#xff1a; ?model.chat