当前位置: 首页 > news >正文

内存函数(C语言)

内存函数

以下函数的头文件:string.h

  • 针对内存块进行处理的函数

memcpy

函数原型:

void* memcpy(void* destination, const void* source, size_t num);目标空间地址                源空间地址
  • num,被拷贝的字节个数

  • 返回目标空间的起始地址

  • 从source函数向后拷贝num字节个数据到目标空间

  • 目标空间地址和源空间地址有重叠部分,有重叠内存的拷贝,结果是未定义的

  • C语言标准规定:memcpy可以不负责有重叠内存的拷贝

    • 而vs编译器的库函数也可以实现重叠内存的拷贝。
#include <stdio.h>
#include <string.h>
int main()
{int arr1[20] = { 1, 2, 3, 4, 5, 6 };int arr2[20] = { 0 };memcpy(arr2, arr1, sizeof(int) * 5);for (int i = 0; i < 5; i++){printf("%d ", arr2[i]);}return 0;
}

在这里插入图片描述

通过函数原型也发现,这个memcpy函数是一个函数参数,函数返回类型都是void无符号类型,这是说明该函数是一个,泛型函数,它可以接收任意类型的参数,使同一个函数能用于多种类型的数据

该用例说明了,将数组arr1从第一个元素开始的20个字节拷贝到arr2中。

模拟memcpy函数

void* my_memcpy(void* dest, const void* src, size_t num)
{void* ret = dest;assert(dest && src);while(num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}

该函数是泛型函数,在返回类型和参数上要保持一致,后面的size_t num参数用来接收拷贝的字节个数。

能够进行任意数据类型的拷贝,这里只能将源空间一个一个字节拷贝到目标空间里,而void类型不是4个字节,所以将,dest和src强制类型转换为char类型,这样每循环一次就拷贝一个字节的内容,然后让指针dest和src向后走一个字节。

(char*)dest++;dest = *(char*)dest + 1;的区别:

c/c++里的强制类型转换,是临时的,(char*)dest++;试图将一个 void *类型的dest强制类型转换,然后++,语法是有问题的,对 (char *)dest使用++,实际上是 (char* dest) = dest + 1;,将无符号类型的指针赋给 char *类型的指针编译器会报错的
在这里插入图片描述

最后将目标空间的起始地址返回即可。

使用assert断言是因为,目标空间和源空间都不能为空,否则就是对空指针解引用,加一条assert是代码具有更好的健壮性。

memmove

函数原型:

void* memmove(void* destination, const void* source, size_t num);
  • 负责重叠内存的拷贝
  • 用法与memcpy相同。
  • memmove可以是memcpy的替代,它支持更多的使用场景,重叠内存的拷贝。

模拟memmove函数

俩个函数的功能及其相似,它们在实现上的不同,模拟memcpy函数是,对字节进行拷贝时,是将source里的字节从前向后一个一个拷贝的,如图:

source里一共有12个字节,模拟的memcpy函数将从数字2开始的第一个字节,一个一个拷贝到目标空间里。
在这里插入图片描述

在这里插入图片描述

若两快空间重叠时会发生哪些情况:

  • dest指针大于source指针,目标空间与源空间重叠,但目标空间起始地址小于源空间,此时针对情况一,如上图橘色与蓝色重叠部分
  • dest指针小于source指针,目标空间与源空间重叠,但目标空间起始地址大于源空间,此时针对情况二,如上图绿色与蓝色重叠部分

情况一:

  • 当使用从前向后一个一个字节的拷贝时, 0 被拷贝为 2,1 被拷贝为 3,2 被拷贝为 4,结果与预期相符和,所以当目标空间与源空间重叠,但目标空间起始地址小于源空间时,采用从前向后一个字节一个字节的拷贝。

在这里插入图片描述

情况二:

  • 对应情况二使用从前向后一次拷贝字节时,将源空间内容拷贝到密目标空间,此时 4 被拷贝为 2 , 5 被拷贝为 3 , 6被拷贝为2。

  • 与预期结果并不相符和,预期拷贝后的结果时 0 1 2 3 2 3 4。

  • 实际结果为,0 1 2 3 2 3 2。

在这里插入图片描述

可以发现,使用从前向后一起拷贝字节时,源空间的内容覆盖掉,它还没有拷贝就已经被覆盖了,并将其放置在目标空间的最后一个位置。解决这种办法也很好理解,既然从前向后拷贝不行,那我们从后向前拷贝不久可以了。这样就避免了,还没有将源空间的数据拷贝到目标空间就被覆盖的情况。

将source指针和destination指针向后偏移num个字节 (char*)dest + num (char*)src + num,这样它两就指向最后一个字节,从后向前拷贝,每拷贝完一个字节后 num–

在这里插入图片描述

void* my_memmove(void* dest, const void* src, size_t num)
{void* ret = dest;assert(dest && src);if(dest > src)//后向前{while(num--){*((char*)dest + num) = *((char*)src + num);}}else{while(num--){*(char*)dest = *(char*)src;dest = *(char*)dest + 1;src = *(char*)src + 1;}}return ret;
}

当dest > src时,从后向前,第一步将dest和src偏移到最后一个位置, (char*)dest + num (char*)src + num,然后进行赋值 ,

*((char *)dest ) = *((char *)src );,其代码内讲多块内容合并在一起操作,通过控制num大小,来控制了dest和src偏移的位置,而循环结束的条件的num为0,这样就有了一条很精简的代码。

memset

函数原型:

void* memset(void* ptr, int value, size_t num);被填充空间    改变的内容 设置的字节个数       

memset,是用来设置内存的,将内存以字节为单位,

  • 将每个字节设置为 value的内容

很朴素的用法,没有过多的变化,但结果往往会让人出乎意料~。

int main()
{int arr1[20] = { 1, 2, 3, 4, 5, 6 };int arr2[20] = { 0 };memset(arr2, 9, 5);memset(arr1, 8, 5);for (int i = 0; i < 6; i++){printf("%d ", arr1[i]);}printf("\n");for (int i = 0; i < 5; i++){printf("%d ", arr2[i]);}return 0;
}

这不是一个使用的案例,通过运行代码能够发现数组arr1和arr2的结果相当的大,这是因为memset是在字节上设置内容,这里的第一条memset语句,将数组arr2里的五个字节内容,都放置了一个数字9,而内存里的 09 09 09 09实际上是16进制数,0x09090909,所以在打印的结果上会很大。

数字arr1的结果,同数组arr2一样,即使在传递的数组这块内容已经放的有值,memset还会将其覆盖。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这里以字符串为例,字符串的大小是一个字节,这里我试图将字符数组arr1里的前三个字节内容置为x。

int main()
{char arr1[10] = "abcdef";memset(arr1, 'x', 3);return 0;
}

在这里插入图片描述

观察结果,memset函数指哪打哪~,也没有在最后一个x后多余的放置 '\0'

memcmp

函数原型:

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

比较字节大小:

  • ptr1 > ptr2 返回大于0
  • ptr1 < ptr2 返回小于0
  • 等于返回 0
  • num是指比较字节个数

memcmp函数与字符串函数里的strncmp功能很类似,前者是通过比较字节大小,后者是比较字符大小。

int main()
{int arr1[] = { 1, 2, 5 };int arr2[] = { 1, 2, 6 };printf("%d \n", memcmp(arr1, arr2, 8));return 0;
}

arr1前12个字节:

01 00 00 00 02 00 00 00 05 00 00 00

arr2前12个字节

01 00 00 00 02 00 00 00 06 00 00 00

试图比较前8个字节的大小时,返回的结果是0,每个字节上的内容都是相等的,而 memcmp(arr1, arr2, 9);,比较前9个字节大小时,将返回小于0的数字, 06 > 05 。

cmp(arr1, arr2, 8));
return 0;
}
arr1前12个字节:

01 00 00 00 02 00 00 00 05 00 00 00

arr2前12个字节

01 00 00 00 02 00 00 00 06 00 00 00

试图比较前8个字节的大小时,返回的结果是0,每个字节上的内容都是相等的,而 memcmp(arr1, arr2, 9);,比较前9个字节大小时,将返回小于0的数字, 06 > 05 。

在这里插入图片描述

相关文章:

内存函数(C语言)

内存函数 以下函数的头文件&#xff1a;string.h 针对内存块进行处理的函数 memcpy 函数原型&#xff1a; void* memcpy(void* destination, const void* source, size_t num);目标空间地址 源空间地址num&#xff0c;被拷贝的字节个数 返回目标空间的起始地…...

力扣 哈希表刷题回顾

哈希表理论总结 什么时候用哈希表&#xff0c;快速判断一个元素是否出现在集合中时&#xff0c;用哈希这种空间换时间的方法。 哈希函数与哈希碰撞 哈希函数是指将key映射到对应的哈希表上 哈希碰撞是指映射的过程中容易出现多对一的情况&#xff0c;用什么方法解决拉链法和…...

Qt 统计图编程

学习目标&#xff1a;Qt 折线图&#xff0c;柱形图和扇形统计图编程 学习基础 Qt QChart 曲线图表操作-CSDN博客 学习内容 Qt中绘制三种常见的图表非常方便, 主要步骤如下: 1. 折线图: - 使用QLineSeries定义折线数据,添加多个坐标点 - 使用QValueAxis创建X轴和Y轴 - 将…...

SQL中的谓词与谓词下推

在 SQL 查询中&#xff0c;谓词&#xff08;Predicate&#xff09;是用来对数据进行过滤的条件。它们决定了数据从数据库表中被选择的条件。理解和正确使用 SQL 谓词对于编写高效查询至关重要。 目录 什么是谓词&#xff1f;一个真实的故事SQL 谓词的代码示例比较谓词逻辑谓词…...

浅聊授权-spring security和oauth2

文章目录 前言自定义授权spring security授权oauth2授权概述 前言 通常说到授权&#xff0c;就会想到登录授权、token令牌、JWT等概念&#xff0c;授权。顾名思义就是服务器授予了客户端访问资源的权益&#xff0c;那么要实现授权有几种方案呢&#xff0c;三种授权方式在公司项…...

时间复杂度计算

目录 时间复杂性 ⼤O的渐进表⽰法 时间复杂性 定义&#xff1a;在计算机科学中&#xff0c;算法的时间复杂度是⼀个函数式T(N)&#xff0c;它定量描述了该算法的运⾏时间。 时间复杂度是衡量程序的时间效率&#xff0c;那么为什么不去计算程序的运⾏时间呢&#xff1f; 1.…...

React 18 + Babel 7 + Webpack 5 开发环境搭建

文章目录 一、基础开发环境搭建1. 新建项目目录2. 项目目录结构及内容3. 安装 React 18 Babel 7 Webpack 54. 配置 Babel 和 Webpack5. 调试/构建项目 二、扩展项目支持的能力&#xff08;待补充&#xff09;1. JS 扩展&#xff08;待补充&#xff09;2. CSS 扩展&#xff08…...

MongoDB Shard 集群 Docker 部署

MongoDB Shard Docker 部署 部署环境 主机地址主机配置主机系统Mongodb1/192.168.31.1352CPU 4GBDebian12Mongodb2/192.168.31.1092CPU 4GBDebian12Mongodb3/192.168.31.1652CPU 4GBDebian12 镜像版本 mongodb/mongodb-community-server:5.0.27-ubuntu2004 部署集群 部署…...

MacOS 开发 — Packages 程序 macOS新版本 演示选项卡无法显示

MacOS 开发 — Packages 程序 macOS新版本 演示选项卡无法显示 问题描述 &#xff1a; 之前写过 Packages 的使用以及如何打包macOS程序。最近更新了新的macOS系统&#xff0c;发现Packages的演示选项卡无法显示&#xff0c;我尝试从新安转了Packages 也是没作用&#xff0c;…...

Hive的分区表分桶表

1.分区表&#xff1a; 是Hive中的一种表类型&#xff0c;通过将表中的数据划分为多个子集&#xff08;分区&#xff09;&#xff0c;每个分区对应表中的某个特定的列值&#xff0c;可以提高查询性能和管理数据的效率。分区表的每个分区存储在单独的目录中&#xff0c;分区的定义…...

PostgreSQL17索引优化之支持并行创建BRIN索引

PostgreSQL17索引优化之支持并行创建BRIN索引 最近连续写了几篇关于PostgreSQL17优化器改进的文章&#xff0c;其实感觉还是挺有压力的。对于原理性的知识点&#xff0c;一方面是对这些新功能也不熟悉&#xff0c;为了尽可能对于知识点表述或总结做到准确&#xff0c;因此需要…...

在Vue中,子组件向父组件传递数据

在Vue中&#xff0c;子组件向父组件传递数据通常通过两种方式实现&#xff1a;事件和回调函数。这两种方式允许子组件与其父组件进行通信&#xff0c;传递数据或触发特定的行为。 1. 通过事件传递数据 子组件可以通过触发自定义事件&#xff0c;并将数据作为事件的参数来向父组…...

数据结构(顺序表)

谈起顺序表&#xff0c;那我们就不得不先来了解一下它的上级概念---线性表 线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使⽤的数据结构&#xff0c;常⻅的线性表&#xff1a;顺序表、链表、栈、队列…...

MySQL之基本查询(上)-表的增删查改

目录 Create(创建) 案例建表 插入 单行数据 指定列插入 单行数据 全列插入 多行数据 全列插入 插入是否更新 插入时更新 替换 Retrieve(读取) 建表插入 select列 全列查询 指定列查询 查询字段为表达式 为查询结果指定别名 结果去重 where条件 比较运算符 逻辑运…...

RocketMQ源码学习笔记:Producer发送消息流程

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、Overview2、验证消息3、查找路由4、选择消息发送队列4.1、选择队列的策略4.2、源码阅读4.2.1、轮询规避4.2.2、故障延迟规避4.2.2.1、计算规避时间4.2.2.2、选择队列 4.2.3、ThreadLocal的…...

kotlin flow collect collectLatest 区别

在 Kotlin 协程库中&#xff0c;collect 和 collectLatest 都是用于收集 Flow 中发射的数据的方法&#xff0c;但它们在处理数据和响应新数据的方式上有所不同。 collect collect 是一个挂起函数&#xff0c;用于收集 Flow 中发射的所有数据。它会按顺序处理每一个发射的数据…...

ELK集群搭建

ELK集群搭建 文章目录 ELK集群搭建1.环境准备2.Elasticsearch环境搭建1.创建es账户并设置密码2.选择对应版本进行下载3.编辑配置文件4.设置JVM堆大小 #7.0默认为4G5.创建es数据及日志存储目录6.修改安装目录和存储目录权限 3.系统优化1.增加最大文件打开数2.增加最大进程数3.增…...

zookeeper+kafka消息队列集群部署

一.消息队列 1、什么是消息队列 消息&#xff08;Message&#xff09;是指在应用间传送的数据。消息可以非常简单&#xff0c;比如只包含文本字符串&#xff0c;也可以更复杂&#xff0c;可能包含嵌入对象。 消息队列&#xff08;MessageQueue&#xff09;是一种在软件系统中用…...

LLM_入门指南(零基础搭建大模型)

本文主要介绍大模型的prompt&#xff0c;并且给出实战教程。即使零基础也可以实现大模型的搭建。 内容&#xff1a;初级阶段的修炼心法&#xff0c;帮助凝聚和提升内力&#xff0c;为后续修炼打下基础。 1、prompt 1.1含义和作用 prompt就是提示工程的意思。在大型语言模型中…...

Element Plus 与 Vue 3:构建现代化 Web 应用的完美搭档

引言 Element Plus是基于Vue 3的组件库&#xff0c;它继承了Element UI的优秀基因&#xff0c;为Vue 3应用提供了丰富的界面组件。Element Plus不仅拥有与Element UI相同的高质量组件&#xff0c;还针对Vue 3进行了优化和更新&#xff0c;确保了与Vue 3的无缝集成。 环境准备…...

线程间通信与变量修改感知:几种常用方法

线程间通信与变量修改感知&#xff1a;几种常用方法 1. 使用volatile关键字2. 使用synchronized关键字3. 使用wait/notify/notifyAll机制4. 使用轮询&#xff08;Polling&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java…...

前后端通信 —— HTTP/HTTPS

目录 一、HTTP/HTTPS 简介 1、HTTP 2、HTTPS 二、HTTP 工作过程 三、HTTP 消息 1、HTTP消息结构 2、HTTP消息示例 四、HTTP 方法&#xff08;常用&#xff09; 1、GET 2、POST 3、PUT 4、DELETE 5、GET与POST对比 五、HTTP 状态码&#xff08;常用&#xff09; …...

人工智能 (AI) 应用:一个高精度ASD 诊断和照护支持系统

自闭症谱系障碍&#xff08;ASD&#xff09;是一种多方面的神经发育状况&#xff0c;影响全球大约1/100的儿童&#xff0c;而在中国&#xff0c;这一比例高达1.8%&#xff08;引用自《中国0&#xff5e;6岁儿童孤独症谱系障碍筛查患病现状》&#xff09;&#xff0c;男童为2.6%…...

C# 1.方法

方法组成&#xff1a; 1.修饰符&#xff1a;public一般定义共有的 2.方法返回值&#xff1a;void 无返回值; 非void&#xff0c;可以写成其他类型例如int&#xff0c;float&#xff0c;string,string[]等 3.方法名&#xff1a;Add 大驼峰命名法&#xff0c;每一个首字符大写。…...

【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块

二叉搜索树&#xff1a;【C进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客 目录 一、AVL树的概念 二、AVL树的原理与实现 AVL树的节点 AVL树的插入 AVL树的旋转 AVL树的打印 AVL树的检查 三、实现AVL树的完整代码 四、总结 前言&#xff1a…...

px,em,rem之间的关系换算

px,em,rem之间的换算 px&#xff1a;普通大小 em&#xff1a;相对单位&#xff0c;相对于父元素的字体大小 rem&#xff1a;相对单位&#xff0c;相对于根元素&#xff08;html&#xff09;的字体大小 <!DOCTYPE html> <html lang"en"> <head>…...

HTTP——POST请求详情

POST请求 【传输实体文本】向指定资源提交数据进行处理请求&#xff08;例如提交表单或者上传文件&#xff09;。数据被包含在POST请求体中。POST 请求可能会导致新的资源的建立或已有资源的修改。 场景&#xff1a; 1. 提交用户注册信息。 2. 提交修改的用户信息。 常见的…...

外包干了1个月,技术明显退步。。。

有一种打工人的羡慕&#xff0c;叫做“大厂”。 真是年少不知大厂香&#xff0c;错把青春插稻秧。 但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂…...

LeetCode加油站(贪心算法/暴力,分析其时间和空间复杂度)

题目描述 一.原本暴力算法 最初的想法是&#xff1a;先比较gas数组和cost数组的大小&#xff0c;找到可以作为起始点的站点(因为如果你起始点的油还不能到达下一个站点&#xff0c;就不能作为起始点)。当找到过后&#xff0c;再去依次顺序跑一圈&#xff0c;如果剩余的油为负数…...

5.1 软件工程基础知识-软件工程概述

软件工程诞生原因 软件工程基本原理&#xff08;容易被考到&#xff09; 软件生存周期 能力成熟度模型 - CMM 能力成熟度模型 - CMMI 真题...