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

C语言内存函数(21)

文章目录

  • 前言
  • 一、memcpy的使用和模拟实现
  • 二、memmove的使用和模拟实现
  • 三、memset函数的使用
  • 四、memcmp函数的使用
  • 总结


前言

  正文开始,发车!


一、memcpy的使用和模拟实现

函数模型:void* memcpy(void* destination, const void* source, size_t num);

使用注意事项:
1.函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置
2.这个函数在遇到 ‘\0’ 的时候并不会停下来,给多少就复制多少
3.如果 source 和 destination 有任何的重叠,复制的结果都是未定义

// 使用举例
int main()  
{  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };  int arr2[10] = {0};  //将arr1中的1 2 3 4 5,拷贝到arr2中  memcpy(arr2, arr1, 5*sizeof(int));  int i = 0;  for (i = 0; i < 10; i++)  {  printf("%d ", arr2[i]);  // 1 2 3 4 5 0 0 0 0 0}  return 0;  
}

我们来模拟实现一下 my_memcpy ,首先所接收的 dest 、src 都是不明确类型的,用 void* 接收,返回类型为 void*,且 src 并不希望被修改,可以加个 const 修饰

其实没那么复杂,我们有 num 个字节需要复制,那么直接循环 num 次就可以了,只是需要把 dest 和 src 强制转化成 char* 的类型就OK了
在这里插入图片描述

标红是因为什么原因?

原来,强制类型转化是临时的,下面两句各自加上就好了

dest = (char*)dest + 1;
src = (char*)src + 1;

这时候,我们再来回想一下为什么尽量要避免 dest 和 src 两者发生重叠
你脑海里想象一下 my_memcpy(arr1 + 2, arr1 + 1, 5 * sizeof(int)); 的过程,就明白了

下面是memcpy的完整模拟实现:

void* my_memcpy (void * dst, const void * src, size_t count )  
{  void * ret = dst;  assert(dst);  assert(src);  /*  * copy from lower addresses to higher addresses  */  while (count--) {  *(char *)dst = *(char *)src;  dst = (char *)dst + 1;  src = (char *)src + 1;  }  return(ret);  
}

二、memmove的使用和模拟实现

那怎么处理内存重叠的问题呢,那就用这个!

函数原型:void* memmove(void* destination, const void* source, size_t num);

使用注意事项:
1.可以把 memmove 当作可以处理重叠的 memcpy 吗? 可以!

我们来看看这个 memmove 为什么可以解决内存重叠情况下的复制:

当 src 在 dest 左边且发生重叠的时候,这时候如果从左往右复制,dest 所指向的 3 立马就被覆盖,等到 src 来复制的时候,已经只能复制 1 了,这不是我们想要的,于是,我们考虑从右向左复制,也就是 dest 的 7 被 5 覆盖 , 6 被 4 覆盖 … 3 被 1 覆盖
在这里插入图片描述

当 src 在 dest 右边且发生重叠的时候,这时候从右往左复制,又会发生上述的提前覆盖的情况,解决方法是什么?从左向右复制!
在这里插入图片描述

当 src 与 dest 不发生重叠的时候,从左向右 或者 从右向左 复制都没影响,所以我们想出一个总的复制方案

当 src < dest 的时候,从右向左复制
当 dest < src 的时候,从左向右复制

我们来思考一下 从右往左 复制该怎么实现,首先 dest 和 src 先跳到未部,注意要减一个1

dest = (char*)dest + num - 1;
src = (char*)src + num - 1;

接着开始往回复制,这与从左向右几乎等同,无非就是自加变为自减,所以,完整的 my_memmove 如下

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 {// 从右往左dest = (char*)dest + num - 1;src = (char*)src + num - 1;while (num--) {*(char*)dest = *(char*)src;dest = (char*)dest - 1;src = (char*)src - 1;}}return ret;
}

三、memset函数的使用

函数原型:void* memset(void* ptr, int value, size_t num);

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

// 使用实例
#include <stdio.h>  
#include <string.h>  
int main ()  
{  char str[] = "hello world";  memset (str,'x',6);  printf(str);  // xxxxxxworldreturn 0;  
}

请注意!memset在设置的时候,是以字节为单位来设置的

// 错误使用举例
int main()  
{  int arr[10] = { 0 };  memset(arr, 1, 40);  //errreturn 0;  
}

因为它是以字节为单位来设置的,所以你可以想象,一个 int 有四个字节,每个字节都是1
也就是说,上述数组 arr 的每个元素都是 0x01010101,而不是我们想要的0x00000001

我们一般拿来清空、初始化数组或者结构体

四、memcmp函数的使用

函数原型:int memcmp(const void* ptr1, const void* ptr2, size_t num);

作用是比较从 ptr1 和 ptr2 指针指向的位置开始,向后的 num 个字节

// 使用实例
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[] = { 1,2,3,4,8 };int ret = memcmp(arr1, arr2, 16); // 0printf("%d\n", ret);ret = memcmp(arr1, arr2, 17); // -1printf("%d\n", ret);return 0;
}

至于原理,请调试并打开内存查看,就很清楚了
在这里插入图片描述


总结

我们在学习的时候应该带有自己的思考,比如上述 arr1 和 arr2 的图片可能有一个地方会引起你的注意

对于每个 int 的四个字节,数据低位同时也是存放在地址的低位
举例来说,假如把 int a = 1;存放在内存里面:
0x00000093F16FF6A8 :存放01
0x00000093F16FF6A9 :存放00
0x00000093F16FF6AA :存放00
0x00000093F16FF6AB :存放00

你可能跟我一样,对数据在内存中的存储由此产生了极大的疑问和兴趣,没关系,我们下篇文章开始介绍

相关文章:

C语言内存函数(21)

文章目录 前言一、memcpy的使用和模拟实现二、memmove的使用和模拟实现三、memset函数的使用四、memcmp函数的使用总结 前言 正文开始&#xff0c;发车&#xff01; 一、memcpy的使用和模拟实现 函数模型&#xff1a;void* memcpy(void* destination, const void* source, size…...

三高基本概念之-并发和并行

并行和并发是计算机科学中两个重要但容易混淆的概念&#xff0c;它们之间的主要区别可以从以下几个方面进行阐述&#xff1a; 一、定义与含义 并行&#xff08;Parallel&#xff09;&#xff1a;并行是指两个或多个事件在同一时刻发生&#xff0c;即这些事件在微观和宏观上都…...

宝塔面板FTP连接时“服务器发回了不可路由的地址。使用服务器地址代替。”

参考 https://blog.csdn.net/neizhiwang/article/details/106628899 错误描述 我得服务器是腾讯&#xff0c;然后使用宝塔建了个HTML网站&#xff0c;寻思用ftp上传&#xff0c;结果报错&#xff1a; 状态: 连接建立&#xff0c;等待欢迎消息... 状态: 初始化 TLS 中... 状…...

面试的一些小小经验

无论何时&#xff0c;找到合适的满意的工作&#xff08;距离住处的地理位置&#xff0c;薪资&#xff0c;工作氛围&#xff09;并不是一件容易的事情。个人能力与职位的适配性永远是有误差的客观存在。 十全十美难得&#xff0c;满足个人的个体化优先级才是客观的存在。 1.投简…...

IV转换放大器原理图及PCB设计分析

【前言】 今天给大家分享一下关于IV转换放大器的相关电路设计心得。IV转换使用的场合非常之多&#xff0c;尤其是电流型输出的传感器&#xff0c;比如光敏二极管、硅光电池等等&#xff0c;这些传感器输出的电流信号非常微弱&#xff0c;我们如果需要检测它们&#xff0c;首先得…...

【数学建模经验贴】一个研赛数模老手的经验

我&#xff08;非C君&#xff0c;是一个朋友&#xff09;参加了3次“深圳杯”数模&#xff0c;1次全国大学生数模&#xff0c;以及1次全国研究生数模&#xff0c;2016年参加了全国研究生数模的交流会&#xff0c;但没有参加过美赛&#xff0c;应该算是一个江湖老手了吧。下面内…...

vivo手机已删除的短信还能恢复吗?

虽然现在我们很少使用vivo手机的短信功能&#xff0c;但是我们偶尔还会通过vivo手机短信功能接收一些重要的信息。如果我们在清理垃圾短信的时候误删了vivo手机重要短信&#xff0c;该怎么恢复呢&#xff1f; 方法一&#xff1a;通过vivo云服务恢复 1、确保您已开启vivo云服务…...

[网络][CISCO]CISCO IOS升级

CISCO IOS升级-&#xff08;转&#xff09;2008-06-27 15:35IOS 升级 在介绍CISCO路由器IOS升级方法前&#xff0c;有必要对Cisco路由器的存储器的相关知识作以简单介绍。路由器与计算机相似&#xff0c;它也有内存和操作系统。在Cisco路由器中&#xff0c;其操作系统叫做互连…...

通过python提取PDF文件指定页的图片

整体思路 要从 PDF 文件中提取指定页和指定位置的图片&#xff0c;可以分几个步骤来实现&#xff1a; 1.1 准备所需工具与库 在 Python 中处理 PDF 和图像时&#xff0c;需要使用几个库&#xff1a; PyMuPDF (fitz)&#xff1a;用于读取和处理 PDF 文件&#xff0c;可以精确…...

Leetcode Hot 100刷题记录 -Day12(轮转数组)

轮转数组 问题描述&#xff1a; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4]解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向…...

GitHub每日最火火火项目(9.13)

以下是对这些项目的详细介绍&#xff1a; fishaudio 的 fish-speech&#xff1a; 基本信息&#xff1a;这是一种全新的语音技术解决方案&#xff0c;属于文本到语音&#xff08;Text-to-Speech&#xff0c;TTS&#xff09;技术范畴。技术特点&#xff1a; 多语言支持&#xff…...

力扣--649.Dota2参议院

Dota2 的世界里有两个阵营&#xff1a;Radiant&#xff08;天辉&#xff09;和 Dire&#xff08;夜魇&#xff09; Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中&#xff0c;每一位参…...

vim 安装与配置教程(详细教程)

vim就是一个功能非常强大的文本编辑器&#xff0c;可以自己DIY的那种 &#xff0c;不但可以写代码 &#xff0c;还可编译 &#xff0c;可以让你手不离键盘的完成鼠标的所有操作。 如果想要了解vim的的发展历史和详细解说&#xff0c;可以自行上网搜索&#xff0c;我主要是记录一…...

【WPF】Popup的使用

WPF&#xff08;Windows Presentation Foundation&#xff09;中的Popup控件用于创建弹出窗口&#xff0c;如工具提示、上下文菜单等。Popup控件本身并不直接显示任何内容&#xff0c;它需要一个子元素来显示实际的内容。 以下是一个简单的XAML示例&#xff0c;展示如何创建一…...

力扣刷题之2576.求出最多标记下标

题干描述 给你一个下标从 0 开始的整数数组 nums 。 一开始&#xff0c;所有下标都没有被标记。你可以执行以下操作任意次&#xff1a; 选择两个 互不相同且未标记 的下标 i 和 j &#xff0c;满足 2 * nums[i] < nums[j] &#xff0c;标记下标 i 和 j 。 请你执行上述操…...

黑马JavaWeb开发笔记16——请求(postman、简单参数、实体参数、@RequestParam映射)

文章目录 前言一、postman工具1. 引入2. 介绍3. 安装4. 使用 二、简单参数1. 原始方式&#xff08;仅了解&#xff0c;以后的开发不会使用&#xff09;2. SpringBoot方式3. 参数名不一致(RequestParam映射) 三、实体参数1. 简单实体对象2. 复杂实体对象 总结 前言 本篇文章是2…...

Corrupt block relative dba: 0x02c0b382 (file 11, block 45954)

接前面断电故障处理2&#xff1a;oracle数据库断电无法启动恢复-CSDN博客 DM00 started with pid145, OS id16516, job SYS.SYS_IMPORT_TABLE_01 2024-09-13T20:05:22.33130208:00 ADVISORY: Please collect redo for investigation of ORA-8103. Use command: ALTER SYSTE…...

二叉排序树在实际生活应用中作用

二叉排序树&#xff08;Binary Search Tree, BST&#xff09;在实际生活中有多种应用&#xff0c;主要用于需要快速查找、插入和删除操作的场景。以下是一些常见的应用领域和具体示例&#xff1a; 1.数据库索引 数据库系统中经常使用 BST 作为索引结构。例如&#xff0c;B-tr…...

单例模式的学习

示例&#xff1a; #ifndef TEST_H #define TEST_Hclass test { public:static test * GetINSTANCE();void print(); private:test(); };#endif // TEST_H#include "test.h" #include <QMutex> #include <QDebug> test::test() {}test *test::GetINSTANC…...

54 mysql 中各种 timeout - connect/wait/interactive/read/write_timeout

前言 在 mysql 的服务器配置中, 我们经常会使用到几个 timeout 诸如 connect_timeout, wait_timeout, interactive_timeout, read_timeout, write_timeout 等等 我们 这里来看一下 他们的具体的使用场景, 以及具体控制的相关信息 是什么 connect_timeout 这个是 客户端 和…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...