【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.hqwc.cn/news/679629.html

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

相关文章

在国企分公司做信息宣传新闻投稿的经验分享

作为一名国企分公司的信息宣传工作者,我亲历了从传统投稿方式到数字化转型的全过程,这段经历既充满了挑战,也收获了成长。回首最初的日子,那些用邮箱投稿的时光,至今仍让我感慨万千。 初尝辛酸,邮箱投稿的艰难岁月 刚接手信息宣传工作时,我满腔热情,却很快被现实的冷水浇了个透…

场景文本检测识别学习 day08(无监督的Loss Function、代理任务、特征金字塔)

无监督的Loss Function&#xff08;无监督的目标函数&#xff09; 根据有无标签&#xff0c;可以将模型的学习方法分为&#xff1a;无监督、有监督两种。而自监督是无监督的一种无监督的目标函数可以分为以下几种&#xff1a; 生成式网络的做法&#xff0c;衡量模型的输出和固…

【仪酷LabVIEW AI工具包案例】使用LabVIEW AI工具包+YOLOv5结合Dobot机械臂实现智能垃圾分类

‍‍&#x1f3e1;博客主页&#xff1a; virobotics(仪酷智能)&#xff1a;LabVIEW深度学习、人工智能博主 &#x1f384;所属专栏&#xff1a;『仪酷LabVIEW AI工具包案例』 &#x1f4d1;上期文章&#xff1a;『【YOLOv9】实战二&#xff1a;手把手教你使用TensorRT实现YOLOv…

新版Idea配置仓库教程

这里模拟的是自己搭建的本地仓库环境&#xff0c;基于虚拟机搭建利用gogs创建的仓库 1、Git环境 你需要准备好git和仓库可以使用github 、gitee等 1.1 拉取代码 本项目使用 Git 进行版本控制&#xff0c;在 gogs 上创建一个个人使用的 git 仓库&#xff1a; http://192.168.…

【C++】详解STL容器之一的 vector

目录 概述 迭代器 数据结构 优点和缺点 接口介绍 begin end rbegin rend resize reseve insert erase 其他一些接口 模拟实现 框架 获取迭代器 深浅拷贝 赋值重载 reseve resize 拷贝构造 构造 析构 insert erase 其他 概述 vector是STL的容器之一。…

漏扫神器Invicti V2024.4.0专业版

前言 Invicti Professional是Invicti Security公司推出的一个产品&#xff0c;它是一种高级的网络安全扫描工具。Invicti Professional旨在帮助组织发现和修复其网络系统中的潜在安全漏洞和弱点。它提供了全面的漏洞扫描功能&#xff0c;包括Web应用程序和网络基础设施的漏洞扫…

如何设置cPanel的自动备份

近期我们购买了Hostease美国VPS云主机产品&#xff0c;由于需要设置服务器的自动备份&#xff0c;我们向Hostease技术团队进行了咨询&#xff0c;他们提到VPS云主机的cPanel面板包含自动备份功能&#xff0c;下面我们就介绍如何进行自动备份的设置。 首先你需要登录到WHM面板&…

含义:理财风险等级R1、R2、R3、R4、R5

理财风险等级R1、R2、R3代表什么&#xff0c;为什么R1不保本&#xff0c;R2可能亏损 不尔聊投资https://author.baidu.com/home?frombjh_article&app_id1704141696580953 我们购买理财产品的时候&#xff0c;首先都会看到相关产品的风险等级。风险等级约定俗成有5级&…

Object类

Object类 概念&#xff1a;Object类是所有类的父类&#xff0c;也就是说任何一个类在定义时候如果没有明确的指定继承一个父类的话&#xff0c;那么它就都默认继承Object类&#xff0c;因此Object类被称为所有类的父类&#xff0c;也叫做基类/超类。 常用方法 方法类型描述eq…

基于 llama2 的提示词工程案例2

优化大型语言模型&#xff08;LLMs&#xff09; 优化大型语言模型&#xff08;LLMs&#xff09;中的提示词&#xff08;prompts&#xff09;是提高模型性能和输出相关性的重要手段。以下是一些优化提示词的方向&#xff1a; 明确性&#xff1a;确保提示词清晰明确&#xff0c;…

javaWeb入门(自用)

1. vue学习 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"https://unpkg.com/vue2"></script> </head> <body><div id"…

linux上如何排查JVM内存过高?

怎么排查JVM内存过高&#xff1f; 前言&#xff1a; 想必工作一两年以后的同学都会逐渐面临到&#xff0c;jvm等问题&#xff0c;但是可能苦于无法熟练的使用一些工具&#xff1b;本文将介绍几个比较常用分析工具的使用方法&#xff0c;带着大家一步步定位分析问题。 1、top 查…