了解内存函数

 ✨✨欢迎👍👍点赞☕️☕️收藏✍✍评论

个人主页:秋邱'博客

所属栏目:C语言

前言 

 内存函数不止malloc、calloc、realloc、free还有memcpy、memmove、memset、memcmp前四个的头文件是<stdlib.h>,后四个的头文件是<string.h>


1.0 memcpy()

函数声明:

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

destination:指向要复制内容的目标数组的指针,类型转换为void*类型的指针。

source:指向被复制的目标数组的指针,类型转换为const void*类型的指针。(const指针内容不能被修改)

num:字节个数。

返回值:返回一个类型的指针。

注意: 

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来
  •  如果source和destination有任何的重叠,复制的结果都是未定义的。

1.1 函数使用

int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[20] = { 0 };
    int num = sizeof(arr1) / sizeof(arr1[0]);
    MyMemcpy(arr1, arr2, num * sizeof(int));
    for (int i = 0; i < num; i++)
    {
        printf("%d ", arr2[i]);
    }
    return 0;
}

打印结果:

1.2 模拟实现

前面对memcpy进行了使用,发现memcpy就是将原内存拷贝到目标内存去的一个函数。

这里用void*来接收指针的地址,因为并不清楚传入函数的是声明类型,所以用void*来接收(void*可以接收任意类型

#include<stdio.h>
void* my_memcopy(void* dest,const void* src, size_t num)
//const这里是拷贝,只需要原地址空间的数据,并不需要修改
{
    void* ret = dest;
    while (num--)
    {
        *(char*)src = *(char*)dest;//将原地址的数据放入目的地址
        src = (char*)src + 1;//对src和dest为(char*)+1跳过一个字节
        dest = (char*)dest + 1;
    }
    return ret;
}
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[20] = { 0 };
    int num = sizeof(arr1) / sizeof(arr1[0]);
    my_memcopy(arr1, arr2, num * sizeof(int));
    for (int i = 0; i < num; i++)
    {
        printf("%d ", arr2[i]);
    }
    return 0;
}

分析:

dest和src均为void*类型,在解引用需要将它们转化为(char*),因为在函数拷贝过程中,并不清楚传入的是int*还是char*等类型,这就需要char*一个一个字节的搬运。

强制类型转换只是临时的,下一次的使用还是原来的类型,也就是dest和src下一次使用还是void*的类型。src = (char*)src + 1将src强换为(char*)赋给src。

开辟一个void*的函数,来存dest的初始地址,最后返回。

对于重叠的内存,交给memmove来处理。


2.0 memmove()

memmove的destination和source相同的

函数声明:

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

destination:指向要复制内容的目标数组的指针,类型转换为void*类型的指针。

source:指向被复制的目标数组的指针,类型转换为const void*类型的指针。(const指针内容不能被修改)

num:字节个数。

返回值:返回一个类型的指针。

2.1 函数使用

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//代码1
    memmove(arr+2, arr, sizeof(int)*5);
    //代码2
    memmove(arr, arr+2, sizeof(int)*5);
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

打印结果:

 代码1

代码2

可以看到,代码1和代码2的打印结果不同,这跟dest在前还是在后有关系,到模拟实现的时候就要分情况来讨论。 

2.2 模拟实现

2.2.1 分析

 2.2.1.1 dest在前,src在后,且有重叠(dest < src)

前向后遍历:3 4 5 6 7 6 7 8 9 10 √

后向前遍历:7 6 7 6 7 6 7 8 9 10 ×

很明显,从前向后遍历才是正确的。

2.2.1.2 src在前,dest在后,且有重叠(dest > src)

前向后遍历:1 2 1 2 1 2 1 8 9 10  ×

后向前遍历:1 2 1 2 3 4 5 8 9 10  √

2.2.1.3 无重叠

前向后遍历=后向前遍历:1 2 3 4 5 1 2 3 4 5

前向后遍历=后向前遍历:5 6 7 8 9 10 5 6 7 8 9 10

使用第三种情况已经被第一二种情况包括了,就不需要拿出来分析了。 只需要讨论前两种就行了。

void* my_memmove(void* dest, const void* src, size_t num)
{
    void* ret = dest;
    if (dest < src)//dest在前,src在后
    {
        
        while(num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }
    else
    {
        //src在前,dest在后
        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;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    my_memmove(arr+2, arr, sizeof(int)*5);
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

dest在前,src在后,且有重叠(dest < src)这个情况跟memcpy的写法一样,只需用if判断dest是否小于src即可。else里只需要将,src里的数据一个字节一个字节的放进dest就可以了。


3.0 memset()

函数声明:

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

ptr:指向需要被放置的指针。

value:该值作为int类型传递,但函数使用该值的unsigned char转换来填充内存块。

num:字节个数。

返回值:返回一个类型的指针。

3.1 函数使用

int main()
{
	char ptr[] = "hello world";
	memset(ptr, 'x', 6);
	printf("%s", ptr);
	return 0;
}

打印结果:

 

3.2 模拟实现

void* my_memset(void* ptr, int value, size_t num)
{
	void* ret = ptr;
	//while(num--)
	//{
	//	*(char*)ptr = (char)value;
	//	ptr = (char*)ptr + 1;
	//}
	for (int i = 0; i < num; i++)
	{
		*(char*)ptr = (char)value;
		ptr = (char*)ptr + 1;
	}
	return ret;
}
int main()
{
	char ptr[] = "hello world";
	my_memset(ptr, 'x', 6);
	printf("%s", ptr);
	return 0;
}

分析: 这个函数的实现比较简单,给定一个指针ptr开始,逐个字节地将值(char*)value设置到后续的内存位置,循环执行num次。


4.0 memcmp()

函数声明:

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

ptr1:指向内存块的指针。

ptr2:指向内存块的指针。

num:字节个数。

 返回值:

返回值意思(如果作为unsigned char值计算)
<0ptr1的值小于ptr2的值
=0ptr1的值等于ptr2的值
>0ptr1的值大于ptr2的值

4.1 模拟实现

int memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	if (*(char*)ptr1 > *(char*)ptr2)
		return 1;
	else if (*(char*)ptr1 < *(char*)ptr2)
		return -1;
	else
	{
		ptr1 = *(char*)ptr1 + 1;
		ptr2 = *(char*)ptr2 + 1;
	}
	return 0;
}
int main()
{
	char arr1[] = "Hello Qiu";
	char arr2[] = "Hello qiu";
	int ret = memcmp(arr1, arr2, sizeof(arr1));
	if (ret > 0)
		printf("'%s' is greater than '%s'.\n", arr1, arr2);
	else if (ret < 0)
		printf("'%s' is less than '%s'.\n", arr1, arr2);
	else
		printf("'%s' is the same as '%s'.\n", arr1, arr2);
	return 0;
}

将传入的指向用void*接收,ptr1和ptr2一个字节一个字节的进行比较,若ptr1大,则返回大于0的值;ptr1相等ptr2,且*ptr1+1、*ptr2+1直到全部比完相同,返回0;ptr2大,返回小于0的值。


感谢各位大佬莅临,如有错误欢迎指出,共同学习进步。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML学习|网页基本信息、网页基本标签、图像标签、超链接标签、列表标签、表格标签、媒体元素、页面结构分析、iframe内联框架

网页基本信息 DOCTYPE是设置使用什么规范&#xff0c;网页整个信息都在html标签中&#xff0c;head标签里包含字符集设置&#xff0c;网页介绍等信息&#xff0c;title标签是网页的名称&#xff0c;网页的主干都在body标签中 网页基本标签 标题标签 h1~h6都是标题标签&#x…

【项目实战】使用Yolov8 + tesseract 实现身份证信息解析(OCR) + 输入可为图片或者pdf + 完整代码 + 整体方案 + 全网首发

本项目可用于实验,毕业设计参考等。整体效果如下所示: 说明:图片来源于网络,如有侵权,请联系作者删除。 目录 一 数据集制作

WPF之多种视图切换

1&#xff0c;View切换&#xff0c;效果呈现 视图1 视图2 视图3 2&#xff0c;在Xaml中添加Listview控件&#xff0c;Combobox控件。 <Grid ><Grid.RowDefinitions><RowDefinition Height"143*"/><RowDefinition Height"30"/>&l…

Ubuntu 下串口工具:Minicom、CuteCom 和 Screen

在 Ubuntu 中&#xff0c;对于串口通信工具的选择&#xff0c;虽然没有一个绝对的 “最好用” 的排名&#xff0c;但根据用户反馈和工具的流行程度&#xff0c;Minicom、CuteCom 和 Screen 这三个工具通常被认为是较为受欢迎和实用的。 一、简介&#xff1a; Minicom&#xff…

一款功能强大的网络安全综合工具-PotatoTool

一、 简介 这款工具是一款功能强大的网络安全综合工具&#xff0c;旨在为安全从业者、红蓝对抗人员和网络安全爱好者提供全面的网络安全解决方案。它集成了多种实用功能&#xff0c;包括解密、分析、扫描、溯源等&#xff0c;为用户提供了便捷的操作界面和丰富的功能选择。 二…

英语学习笔记6——What make is it?

What make is it? 它是什么牌子的&#xff1f; make n.&#xff08;产品的&#xff09;品牌名称    v. 制作 区别&#xff1a;model n.&#xff08;产品的&#xff09;型号       n. 模型       n. 模特 make 指的是大的品牌名称&#xff0c; model 是旗下产品…

Honor of Kings QQ 1537937510

司空震到底要不要物理伤害高呢&#xff1f;还是法术伤害高呢&#xff1f;要不要出魔女和制裁引发的血案 先看下司空震的说明&#xff1a; 说下这个伙计为啥加QQ来骂我&#xff0c;因为这场当然最终是赢了&#xff0c;比赛里他一直强调司空震是物理伤害改版问题&#xff0c;然后…

Whistle Web Debugging Proxy介绍及使用

大家好&#xff0c;今天继续给大家分享一款抓包工具&#xff0c;这款抓包工具是网页的形式&#xff0c;方便多人访问同时维护。Whistle Web Debugging Proxy是一个用于HTTP、HTTPS、WebSocket等网络协议的跨平台调试工具。它可以帮助开发者对网络请求进行捕捉、分析、修改和重定…

WPF/C#:ProgressBar的基本使用

前言 在日常开发过程中&#xff0c;如果遇到需要一段时间才能完成的任务&#xff0c;通常需要给用户一个进度条提示。今天给大家介绍的是WPF/C#中ProgressBar的基本使用。 ProgressBar的介绍 在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Pr…

Kafka应用Demo:多消费者实例按主题订阅消费消息,增强系统可靠性

环境搭建 在本地启动两个消费者进程&#xff0c;配置同的群组&#xff08;neo1&#xff09;, 订阅同一个主题消费消息。 生产者和消费者代码与《Kafka应用Demo&#xff1a;按主题订阅消费消息》相同。 测试步骤 生产者发送多个数字消息到kafka队列&#xff0c; 可以看到只有…

MouseBoost PRO mac中文激活版:专业鼠标助手

MouseBoost PRO mac鼠标性能优化软件&#xff0c;以其强大的功能和智能化的操作&#xff0c;助您轻松驾驭鼠标&#xff0c;提高工作效率。 MouseBoost PRO支持自定义快捷键设置&#xff0c;让您轻松实现快速切换应用程序、打开特定文件、调节音量大小等操作。自动识别窗口功能则…

运行npm install时报错“npm ERR! code 1”

目录 一、问题分析 二、解决问题 一、问题分析 有registry淘宝镜像地址过期的问题&#xff0c;改一下地址 npm淘宝镜像过期解决办法-CSDN博客主要问题是node-sass和sass-loader版本冲突 打开cmd&#xff0c;输入"node -v"查看node版本 我的版本是16&#xff0c;应…

java项目之智慧图书管理系统设计与实现(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智慧图书管理系统设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 智慧图书管理…

elk + filebeat 8.4.3 收集nginx日志(docker部署)

ELK filebeat docker部署 一、 elasticsearch部署1、运行elasticsearch临时配置容器2、拷贝文件目录到本地3、检查elasticsearch.yml4、删除之前elastic&#xff0c;运行正式容器5、docker logs记录启动日志 二、部署kibana1、运行kibana临时配置容器2、docker拷贝配置文件到本…

Java 11 到 Java 8 的兼容性转换

Java 11 到 Java 8 的兼容性转换 欲倚绿窗伴卿卿&#xff0c;颇悔今生误道行。有心持钵丛林去&#xff0c;又负美人一片情。 静坐修观法眼开&#xff0c;祈求三宝降灵台&#xff0c;观中诸圣何曾见&#xff1f;不请情人却自来。 入山投谒得道僧&#xff0c;求教上师说因明。争奈…

HFSS学习-day3-HFSS的工作界面

工作界面也称为用户界面&#xff0c;是HFSS软件使用者的工作环境:了解、熟悉这个工作环境是掌握HFSS软件使用的第一步 HFSS工作环境介绍 1.HFSS工作界面简单的组成说明2.工作界面中各个工作窗口功能主菜单工具栏项目管理窗口属性窗口信息管理窗口进程窗口三维模型窗口 3.HFSS主…

【qt】动态属性(下)

动态属性目录 一.最简单的属性二.访问类的所有属性三.运行时添加属性&#xff08;动态属性&#xff09;★四.总结 一.最简单的属性 想要对一个数据成员进行读和写我们一般都会使用set或者get这种类型的自定义的函数去间接访问&#xff0c;且使用非常的频繁。 因此咱们有一个最…

基于PSO粒子群优化的配电网可靠性指标matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 PSO算法应用于配电网优化的基本原理 5.完整程序 1.程序功能描述 基于PSO粒子群优化的配电网可靠性指标matlab仿真&#xff0c;指标包括saifi, saidi, caidi, aens四个。 2.测试软件版本…

【CTF Web】XCTF GFSJ0480 simple_js Writeup(JS分析+ASCII码)

simple_js 小宁发现了一个网页&#xff0c;但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 解法 查看网页源代码。 function dechiffre(pass_enc){var pass "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";var tab pass_enc.split(,);var …

VSCode-vue3.0-安装与配置-export default简单例子

文章目录 1.下载VSCode2.修改语言为中文3.辅助插件列表4.vue3模板文件简单例子5.总结 1.下载VSCode 从官网下载VSCode&#xff0c;并按下一步安装成功。 2.修改语言为中文 点击确认修改&#xff0c;如下图所示&#xff1a; 或者打开命令面板&#xff1a;输入Configure Displ…