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…

JavaWeb中,web应用的上下文路径解读

当前Web应用的上下文路径&#xff08;Context Path&#xff09;指的是Web应用在服务器上的根路径。在Servlet或JSP环境中&#xff0c;一个服务器可以运行多个Web应用&#xff0c;每个应用都有一个唯一的上下文路径。 例如&#xff0c;如果你的Web应用部署在Tomcat服务器上&…

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;如投影、过滤、统计和…

02-2.3.6 顺序表和链表的比较

喜欢《数据结构》部分笔记的小伙伴可以订阅专栏&#xff0c;今后还会不断更新。&#x1f9d1;‍&#x1f4bb; 此外&#xff0c;《程序员必备技能》专栏和《程序员必备工具》专栏&#xff08;该专栏暂未开设&#xff09;日后会逐步更新&#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…

anaconda 多环境配置

1、查看所有的环境 conda info --envs 2、创建新的环境如python 3.10版本&#xff0c;-n 是name 的简写 conda create -n py3.10 python3.10 3、激活3.10环境 conda activate py3.10 4、退出当前环境 conda deactivate

sentaurus报错记录1

设置(sde:set-process-up-direction "0") 或者(sde:set-process-up-direction "z") 运行后报错 sde:set-process-up-direction: wrong argument type string (expected integer) 可能原因&#xff1a;版本问题导致&#xff0c;注释掉后运行结果正常

空对象模式

空对象模式 空对象模式(Null Object Pattern)是一种设计模式,用于解决在某些情况下不需要实例化具体的对象,而是返回一个“空”对象,这样可以简化代码、避免 NullPointerException 和提高程序的可读性和维护性。简单来说,”空对象模式“就是本该返回None值或抛出异常时,…

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&…

外界的声音都是参考,你不开心就不要参考

在纷繁复杂的世界中&#xff0c;我们每天都会接收到来自四面八方的声音和建议。这些声音可能来自家人、朋友、同事&#xff0c;甚至是社交媒体上的陌生人。然而&#xff0c;在这众多的声音中&#xff0c;我们如何找到自己的方向&#xff0c;保持内心的平静和快乐呢&#xff1f;…

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…