C语言笔记第10篇:内存函数

上一篇的字符串函数只是针对字符串的函数,而内存函数是针对内存块的,不在乎内存中存储的数据!这就是字符串函数和内存函数的区别。

准备好爆米花,正片开始

1、memcpy的使用和模拟实现

memcpy库函数的功能:任意类型数组的拷贝

memcpy的函数声明:

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

destination是目标空间,source是源,size_t num是拷贝字节的个数。

为什么还有输入拷贝字节个数呢?

因为memcpy可以拷贝任意类型的数组,可以是字符,可以是int,也可以是struct自定义类型的,但是前提是要输入要拷贝的字节个数,因为传过去的地址被void类型的指针接收,所以不能得知元素大小。

memcpy函数的调用:

#include <stdlib.h>
#include <stdio.h>
int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr1[i]);}return 0;
}

memcpy函数的模拟实现:

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{assert(dest && source);void* ret = dest;while (num--){*(char*)dest = *(char*)source;dest = (char*)dest + 1;source = (char*)source + 1;}return ret;
}
int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

但是这个函数有一个缺点,就是不能重叠内存拷贝,什么意思呢?就是不能在同一个数组中拷贝,会导致打印信息不正确,例如:

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{assert(dest && source);void* ret = dest;while (num--){*(char*)dest = *(char*)source;dest = (char*)dest + 1;source = (char*)source + 1;}return ret;
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr+2, arr, 20);更改位置int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

我想将arr+2也就是第3个元素的位置开始,拷贝1-5,我们想象的答案是1 2 1 2 3 4 5 8 9 10,但是实际上的答案是1 2 1 2 1 2 1 8 9 10,因为是从前往后开始拷贝,拷贝信息和拷贝的位置重叠了,导致拷贝时更改了拷贝信息,打印出的结果有所差异。

那怎么办?其实还有memmove函数,他和memcpy的拷贝一样,任意类型都可以拷贝,不同的是memmove可以处理重叠内存拷贝。

2、memmove的使用和模拟实现

memmove库函数功能:拷贝任意类型的数组,也可以处理重叠内存拷贝问题

memmove函数的声明:

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

可以看到memmove和memcpy的返回类型和参数一模一样,唯一不同的只是memmove函数的实现细节

memmove函数的调用:

#include <stdlib.h>
#include <stdio.h>
int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

memmove究竟是如何处理拷贝重叠的的呢?请继续往下看

memmove函数的模拟实现:

#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* source, size_t num)
{assert(dest && source);void* ret = dest;if (dest < source)//如果拷贝的地址小于拷贝信息的地址就可以从前向后拷贝{while (num--){*(char*)dest = *(char*)source;//从前向后拷贝dest = (char*)dest + 1;source = (char*)source + 1;}}else//如果拷贝的地址大于或等于拷贝信息的地址就从后向前拷贝{while (num--){*((char*)dest + num) = *((char*)source + num);//从后向前拷贝}}return ret;
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr + 2, arr, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

memmove模拟实现逻辑:

我们可以使用地址于地址之间的关系运算,简单概述就是两个地址之间比较大小。因为该函数是排序数组的,数组又是连续存放的,所以可以比较两个地址。如果目标空间地址比源地址大,就从后往前拷贝。如果目标空间地址比源地址小,就可以从前往后拷贝。

3、memset的使用和模拟实现

memory - 记忆(内存),set - 设置。memset就是内存设置的意思。

memset库函数功能:将参数ptr的前num个字节设置成指定的value值。

memset的函数声明:

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

比如我有一个字符数组,字符串是 " hello world " ,我想把它改成 " hello xxxxx",那我们就可以使用memset函数。

memset函数的调用:

#include <stdio.h>
#include <stdlib.h>
int main()
{char str[] = "hello world";memset(str + 6, 'x', 5);//参数1:字符数组下标6的位置 参数2:需替换的的源值 参数3:字节为单位,向后拷贝的字节大小printf("%s\n", str);//打印 "hello xxxxx"return 0;
}

打印结果确实是 " hello xxxxx ",但是我们也可以用它来改变整型数组:

#include <stdio.h>
#include <stdlib.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};memset(arr, 1, 20);int sz = sizeof(arr)/sizeof(arr[0]);int i = 0;for(i=0;i<sz;i++){printf("%d ",arr[i]);}return 0;
}

我们想象的是改变前20个字节也就是前5个整型元素,打印为:1,1,1,1,1,6,7,8,9,10,但实际上却却是以每个字节更改为01,并不是我们想象的改为五个1,如下图:

所以你想让它的每个字节都是1是可以做的到的,但是你想让它每个整型都是1这个是做不到的,memset本身就是以字节为单位进行设置的。前面的memcpy和memmove虽然也是以字节为单位来拷贝的,但是它们两边都是在变化着拷贝的,所以能够拷贝正确答案。而这个需拷贝的源始终都是一个值,这个值是不会变化的,每次拷贝一个字节都从这里面的一个字节拷贝到另一个空间。

memset的模拟实现:

#include <stdio.h>
#include <stdlib.h>
void my_memset(void* str, int value, size_t num)
{assert(str != NULL);void* ret = str;while (num--){*(char*)str = (char)value;str = (char*)str + 1;}return ret;
}
int main()
{char str[] = "hello world";int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);my_memset(str+6, 'x', 5);my_memset(arr, 1, 20);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");printf("%s\n", str);return 0;
}

4、memcmp的使用和模拟实现

memcmp库函数的功能:和strncmp的功能一样,strncmp是比较两个字符串的,memcmp是比较两个数组内存的

memcmp函数的声明:

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

memcmp返回值:如果ptr1比ptr2大就返回大于0的数字,如果ptr1比ptr2小就返回小于0的数字,如果相等就返回0

memcmp函数的调用:

#include <stdio.h>
#include <stdlib.h>
int main()
{int arr1[] = { 1,4,3,4,5 };int arr2[] = { 1,3,5,7,9 };int ret = memcmp(arr1, arr2, 5);printf("%d\n", ret);return 0;
}

memcmp函数的模拟实现:

#include <stdio.h>
#include <stdlib.h>
int my_memcmp(void* ptr1, void* ptr2, size_t num)
{assert(ptr1 && ptr2);while (num--){if (*(char*)ptr1 != *(char*)ptr2){return *(char*)ptr1 - *(char*)ptr2;}ptr1 = (char*)ptr1 + 1;ptr2 = (char*)ptr2 + 1;}return 0;
}
int main()
{int arr1[] = { 1,3,3,4,5 };int arr2[] = { 1,4,5,7,9 };int ret = my_memcmp(arr1, arr2, 5);printf("%d\n", ret);return 0;
}

                                                              end

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

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

相关文章

Element ui图片上传

前言 对于广大小白来说&#xff0c;图片上传简直是上传难&#xff0c;难于上青天&#xff01;废话不多说&#xff0c;步入正题&#xff0c;您就瞧好吧&#xff01; 步骤一&#xff1a;前端使用element ui组件&#xff08;upload上传&#xff09; 我个人喜欢使用第二个组件&a…

ingress规则

一 k8s 对外服务之 Ingress LB ingress 1 Ingress 简介 service的作用体现在两个方面 ? ① 对集群内部&#xff0c;它不断跟踪pod的变化&#xff0c;更新endpoint中对应pod的对象&#xff0c;提供了ip不断变化的pod的服务发现机制&#xff1b; ② 对集群外部&#xff0c…

Docker部署青龙面板

青龙面板 文章目录 青龙面板介绍资源列表基础环境一、安装Docker二、安装Docker-Compose三、安装青龙面板3.1、拉取青龙&#xff08;whyour/qinglong&#xff09;镜像3.2、编写docker-compose文件3.3、检查语法启动容器 四、访问青龙面板五、映射本地部署的青龙面板至公网5.1、…

Day06 创建首页ListBox列表数据

​ 完成当前章节后,最终效果图如下 ​​​​ 一.首页汇总方块鼠标悬停阴影效果设计 首先,在上一章节首页设计 的时候,就已经知道当前主界面的汇总方块是使用 Border 来实现的,那么想要实现鼠标悬停时设置阴影的效果,就要在 Border 中进行重写样式。 需要使用 触发器 来实…

【动手学深度学习】卷积神经网络CNN的研究详情

目录 &#x1f30a;1. 研究目的 &#x1f30a;2. 研究准备 &#x1f30a;3. 研究内容 &#x1f30d;3.1 卷积神经网络 &#x1f30d;3.2 练习 &#x1f30a;4. 研究体会 &#x1f30a;1. 研究目的 特征提取和模式识别&#xff1a;CNN 在计算机视觉领域被广泛用于提取图像…

Locality-aware subgraphs for inductive link prediction in knowledge graphs

Locality-aware subgraphs for inductive link prediction in knowledge graphs a b s t r a c t 最近的知识图&#xff08;KG&#xff09;归纳推理方法将链接预测问题转化为图分类任务。 他们首先根据目标实体的 k 跳邻域提取每个目标链接周围的子图&#xff0c;使用图神经网…

Spark SQL - 操作数据帧

本教程将通过一个具体的案例来演示如何在Spark SQL中操作数据帧。我们将从获取学生数据帧开始&#xff0c;包括两种方法&#xff1a;一是由数据集转换而来&#xff0c;二是直接读取文件生成数据帧。然后&#xff0c;我们将对数据帧进行各种操作&#xff0c;如投影、过滤、统计和…

Linux系统进行DNS域名解析

文章目录 一、DNS1.1 DNS概述1.2 DNS的通俗解释1.3 域名的体系结构1.4 DNS解析的参数 1.5 DNS域名解析的过程 二、如何实现DNS(内网)2.1 DNS正向解析2.2 DNS反向解析2.3 主从DNS 一、DNS 1.1 DNS概述 DNS &#xff1a;Domain Name System&#xff0c;是域名系统的简称&#x…

Flutter Bloc之简单记录

目录 0.库安装 1.插件和自动生成 2.状态的配置 1.初始化中&#xff1a; 2.赋值完成后&#xff1a; 3.如果出错&#xff1a; 3.事件的配置 1.定义一个读取事件 2.定义一个更改事件 4.Bloc的设置 5.Bloc的使用 1.BlocProvider 2.内部调用 参考文章进行类的配置 0.库…

NSS题目练习7

[MoeCTF 2022]baby_file 打开看见一串源代码&#xff0c;需要get传参传入file 题目提示php伪协议 用dirsearch扫描发现flag.php 用php伪协议查看&#xff0c;回显一串base64编码 解码后得到flag [鹤城杯 2021]Middle magic 读取这两个文件 一个php正则表达式 补充&#xff1a…

解锁ArrayBlockingQueue奥秘:深入源码的精彩之旅

1.简介 ArrayBlockingQueue 是 BlockingQueue 接口的一个实现类&#xff0c;它基于数组实现了一个有界阻塞队列。创建 ArrayBlockingQueue 实例时需要指定队列的容量&#xff0c;队列的大小是固定的&#xff0c;无法动态增长。 主要特点包括&#xff1a; 有界性&#xff1a;A…

STM32——hal_SPI_(介绍)

SPI&#xff08;串行外围设备接口&#xff09;是一种高速的、全双工、同步的通信协议&#xff0c;通常用于短距离通信&#xff0c;尤其是在嵌入式系统中与各种外围设备进行通信。SPI接口由摩托罗拉公司推出&#xff0c;由于其简单和灵活的特性&#xff0c;它被广泛用于多种应用…

2023年计算机图形学课程知识总结

去年就该写的&#xff0c;但是去年这个时候太忙了。 就写来自己看看。留个记录留个念 文章目录 1. 图形&#xff0c;图像的定义2. 点阵、矢量3. 走样&#xff0c;反走样4. 字符裁剪精度&#xff08;1&#xff09; 串精度&#xff08;2&#xff09; 字符精度&#xff08;3&…

SpringBoot打war包并配置外部Tomcat运行

简介 由于其他原因&#xff0c;我们需要使用SpringBoot打成war包放在外部的Tomcat中运行,本文就以一个案例来说明从SpringBoot打war包到Tomcat配置并运行的全流程经过 环境 SpringBoot 2.6.15 Tomcat 8.5.100 JDK 1.8.0_281 Windows 正文 一、SpringBoot配置打war包 第一步&a…

J-Lin烧录

1、J-linK介绍 J-Link是由德国SEGGER公司推出的&#xff0c;主要用于支持仿真ARM内核芯片的JTAG仿真器。它支持JTAG和SWD两种模式&#xff0c;可以配合多种集成开发环境&#xff08;如IAR EWAR, ADS, KEIL, WINARM, RealView等&#xff09;使用&#xff0c;支持ARM7/ARM9/ARM…

odoo16 档案管理

档案管理&#xff0c;odoo15升级至odoo16完善 电子档案管理是指将传统纸质档案数字化&#xff0c;以便更加方便、快捷、安全地进行档案管理。电子档案管理系统可以对档案进行数字化、存储、检索、共享、传递和销毁等操作&#xff0c;从而提高了档案管理的效率和准确性&#xf…

使用cesiumLab使shp转为3dtlies

过程不做赘述&#xff0c;网上大把&#xff0c;说下注意事项。 1. 存储3DTiles 选项 若是打开则输出的文件为glb格式文件,因为glb文件好储存易传输跨平台。cesium可以使用但无法处理&#xff0c;例如改变颜色&#xff0c;改着色器等。若是不打开则输出的文件为bm3d格式文件,此…

Bond网卡

一、Bond网卡 1.1 Bond网卡概述 Bond网卡是指使用 Linux 系统中的 Bonding 技术创建的虚拟网络接口。 Bonding 技术允许将多个物理网卡&#xff08;也称为接口或端口&#xff09;绑定在一起&#xff0c;形成一个虚拟的网络接口&#xff0c;以增加网络带宽、提高网络容错性和…

表达式求值中的“整型提升”概念

一.基本原理和概念 如&#xff1a;代码 char a&#xff0c;b&#xff0c;c &#xff1b; a b c &#xff1b; 该代码在计算的时候就会先将 b 和 c 提升为 int 类型进行加法后&#xff0c;再将数据进行截断存放在内存存放变量 a 的空间中。 &#xff08;1&#xff09;提升和截…

眼底照 + OCT图 + 精神状态 ,预测阿尔兹海默症

眼底照片和OCT图像&#xff0c;预测阿尔兹海默症 数据多模态网络模型集成可视化分析 论文&#xff1a;https://www.ophthalmologyretina.org/action/showPdf?piiS2468-6530%2824%2900045-9 目前&#xff0c;认知障碍的诊断依赖于血清和蛋白质生物标志物的检测、脑脊液检查和正…