C语言常用内存函数的深度解析

news2025/1/12 15:51:11

文章目录

  • 前言
  • `memcpy`
    • memcpy函数的使用
    • memcpy函数的自我实现
  • `memmove`
    • memmove函数的使用
    • memmove函数的自我实现
  • `memcmp`
    • memcmp函数的使用
    • memcmp函数的自我实现
  • memset
    • memset函数的使用
    • memset函数的自我实现
  • 写在最后

前言

  • 内存函数的使用广泛度大于常用字符串函数的使用广泛度,因为字符串函数只适用于与字符相关的,而内存函数适用于各个类型,因为他是从内存出发,对内存进行修改,因此,学会内存函数,可谓收获满满呀。
  • 这些内存函数的头文件是<string.h>

memcpy

  • 该函数的功能是内存拷贝,相当于字符串函数strncpy的功能,只不过memcpy的运用范围更宽。
  • 该函数是在内存中一对字节一对字节的拷贝。

该函数的函数参数:

在这里插入图片描述
在这里插入图片描述

  • 可以看到,对于重叠的拷贝,memcpy是做不到的(也就是一个数组arr[] = {1,2,3,4,5,6,7,8,9,10},1,2,3,4,5要拷贝到3,4,5,6,7上去,这样是不行的),此时应该用memmove,但是vsmemcpy超额完成了任务,也可以进行重叠拷贝。

memcpy函数的使用

#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 9,9,9,9,9 };

	memcpy(arr1, arr2, sizeof(int) * (sizeof(arr2) / sizeof(arr2[0])));
	// sizeof(int) * (sizeof(arr2) / sizeof(arr2[0])) arr2的总字节个数;

	for (int i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
	{
		printf("%d ", arr1[i]);
	}

	return 0;
}

在这里插入图片描述

运行结果:9 9 9 9 9 6 7 8 9 10

memcpy对字符串也是一样:

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "xxxxxxxxxx";
	char arr2[] = "abcdef";

	// 这里拷贝6个字节,注意最好拷贝的时候不要把\0弄没了,不然打印会出错
	printf("%s\n", (char*)memcpy(arr1, arr2, 6));
	// 因为返回的是 void* 所以最好强转一下 

	return 0;
}

运行结果:abcdefxxxx

memcpy函数的自我实现

代码实现的核心在于,如何一个字节一个字节的拷贝,如何拷贝完一个字节找到后一个字节。

#include <stdio.h>
#include <assert.h>

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);

	// 先保存目的地的开头地址,以便于返回
	void* ret = dest;

	while (num--) // 当num为0拷贝字节数已够,拷贝结束
	{
		// 强转成char*的指针,因为这样,才是一个字节一个字节的拷贝
		*(char*)dest = *(char*)src;

		// 强转成char*指针++向后走跳过一个字节,为下一次的字节拷贝做准备;
		++(char*)dest;
		++(char*)src;
	}

	return ret;
}

int main()
{
	int a1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int a2[] = { 9,9,9,9,9 };

	my_memcpy(a1, a2, 20);
	// 20:a2的总字节数

	for (int i = 0; i < sizeof(a1) / sizeof(a1[0]); ++i)
	{
		printf("%d ", a1[i]);
	}

	return 0;
}

运行结果为:9 9 9 9 9 6 7 8 9 10

如果我们用自我实现得功能来进行重叠拷贝:

在这里插入图片描述

运行结果为:

在这里插入图片描述

  • 为什么会这样呢?
  • 12拷贝过去,此时arr原有的第三个和第四个元素(34所在位置)也被改为了12,当拷贝第三个元素时,是将1拷贝过去而不是原先的3了。
  • 但如果是memmove就不会有这样的情况.

memmove

该函数的功能也相当于是“内存拷贝”,它包含了memcpy的功能,同时比memcpy函数更为的强大,它可以对重叠的内容进行拷贝,也就是一个数组arr[] = {1,2,3,4,5,6,7,8,9,10},1,2,3,4,5要拷贝到3,4,5,6,7上去,最终数组内容变为1,2,1,2,3,4,5,8,9,10

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

memmove函数的使用

#include <stdio.h>
#include <string.h>

int main()
{
	int a1[] = { 1,2,3,4,5,6,7,8,9 };
	int a2[] = { 6,6,6,6,6 };

	memmove(a1, a2, 20);

	for (int i = 0; i < sizeof(a1) / sizeof(a1[0]); ++i)
	{
		printf("%d ", a1[i]);
	}

	return 0;
}

运行结果为:

在这里插入图片描述

  • 可以看到,这种拷贝的功能与memcpy是一样的,接下来进行不一样的重叠拷贝:
#include <stdio.h>
#include <string.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	memmove(arr + 2, arr, 20);
	// 将 1 2 3 4 5 拷贝到 3 4 5 6 7 上面去

	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

在这里插入图片描述

  • arr + 2 当作destarr当作src,也就是说此时src要小于dest,即得出当src < dest时由后往前拷贝;

  • 这里是memmove(arr + 2, arr, 20),如果是memmove(arr, arr + 2, 20)(将3,4,5,6,7拷贝到1,2,3,4,5上面去)呢?此时arr + 2src,arr为dest,这时候就需要从前向后拷贝了,也就是src > dest的情况,即得出当src > dest时由前向后拷贝。

  • 还有一种情况:memmove(arr, arr + 5, 20)是把6,7,8,9,10拷贝到1,2,3,4,5上去,这时虽然src大于dest,但是srcdest的字节差值大于等于了num,也就是说无论是从前向后拷贝还是从后向前拷贝都是一样的,此时的功能就可以理解为是memcpy的功能了。

memmove函数的自我实现

  • 通过上面的使用和分析,有三种情况都是要考虑到:
    1.从前向后拷贝src > dest
    2.从后向前拷贝src < dest
    3.从后向前还是从前向后都是可以的sizeof(type) * |(src - dest)| >= num

在这里插入图片描述

代码实现:

#include <stdio.h>
#include <assert.h>

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);

	void* ret = dest;

	// 落在第三区间总会进一种拷贝方式
	if (src < dest)
	{
		// 从后往前
		while (num--) // 因为判断后 num-- 一次,第一次进来如果num开始为20,
		{             // 进来后等于19找到最后一个字节拷贝

			// 随着num--,拷贝的字节从后往前依次跳一个字节
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	else
	{
		// 从前往后
		while (num--)
		{
			// 这里跟上面的memcpy的自我实现差不多,但是memcpy也可以以从后往前的方式实现
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	
	return ret;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	my_memmove(arr + 2, arr, 20);

	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

运行结果为:1 2 1 2 3 4 5 8 9 10

实现了重叠拷贝,当然其它案例也行,这里大家自行测试了。

memcmp

  • 该函数是比较函数,与strncmp的功能相同,只不过memcmp的对比类型不单只是字符类型了。
  • memcmp是一个字节一个字节的对比。

函数参数如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

函数的返回值:如果前num个字节ptr1ptr2都相等,则返回0,如果找到第一个不相等的字节,返回ptr1的这个字节减去ptr2的这个字节的差值。

memcmp函数的使用

1.

#include <stdio.h>
#include <string.h>

int main()
{
	int a1[] = { 1,2,3,4,5,6,7,8 };
	int a2[] = { 1,2 };

	printf("%d\n", memcmp(a1, a2, 8));
	// 8个字节,比较前两个元素

	return 0;
}

在这里插入图片描述
2.

#include <stdio.h>
#include <string.h>

int main()
{
	int a1[] = { 1,2,3,4,5,6,7,8 };
	int a2[] = { 1,2,5 };

	printf("%d\n", memcmp(a1, a2, 12));
	// 12个字节,比较前三个元素

	return 0;
}

在这里插入图片描述

由于vsmemcmp标准是小于就返回-1,大于就返回1,所以这里的结果为-1

memcmp函数的自我实现

经过上面的介绍,我们大概知道了这个函数的功能,接下来自我实现这个函数。

#include <stdio.h>
#include <assert.h>

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);
    
    // 比对次数--
	while (num--)
	{
	    // 如果有不相同的字节,这里为真,进去返回差值
		if (*(char*)ptr1 - *(char*)ptr2)
			return *(char*)ptr1 - *(char*)ptr2;

		++(char*)ptr1;
		++(char*)ptr2;
	}
    
    // 比对完了没有找到不相同的字节说明都相同,返回0;
	return 0;
}

int main()
{
	int a1[] = { 1,2,3,4,5,6,7,8 };
	int a2[] = { 1,2,5 };

	printf("%d\n", my_memcmp(a1, a2, 8));
	// 12个字节,比较前三个元素

	return 0;
}

运行结果为:-2

memset

该函数的功能是填充内存块,也就是将一个数组里面你指定的内容以修改字节的形式修改成你想要的数据。

在这里插入图片描述
在这里插入图片描述

这里的value虽然是int,但是字符也可以修改,因为字符类型本身就是整型家族,字符可以通过ASCLL码值来进行转换。

memset函数的使用

#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = "xxxxxxxxxx";

	printf("%s\n", (char*)memset(arr, 'y', 5));

	return 0;
}

在这里插入图片描述
2.

#include <stdio.h>
#include <string.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	printf("%s\n", (char*)memset(arr, 1, 40));
	// 40表示整个arr的字节个数

	return 0;
}

运行结果为:
16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009

为什么运行结果会是这样子呢?

  • 我们将数组arr40个字节都改为1,一个整型有4个字节,如果每一个字节上面都是1,在内存当中每个整型的存放是这样的:01 01 01 01,所以当我们从内存中将这个数据读取出来的时候,那将会是一个很大的数,就如上面的运行结果一样的。
  • 所以对于整型数组我们一定要避免出现这样的情况,要牢记这是一个字节一个字节的修改。

memset函数的自我实现

通过上面的认识,下面自我实现memset函数:

#include <stdio.h>
#include <assert.h>

void* my_memset(void* ptr, int value, size_t num)
{
	assert(ptr);

	void* ret = ptr;

	while (num--)
	{
		*(char*)ptr = value;
		++(char*)ptr;
	}

	return ret;
}

int main()
{
	char a[] = "abcdefgh";

	printf("%s\n", (char*)my_memset(a, '@', 5));

	return 0;
}

运行结果为:@@@@@fgh

写在最后

在C语言中,熟练的使用内存函数可以说对内存的理解也是很不错的,能够以内存的视角观察代码,说明你的水平已经很不错了。

感谢阅读本小白的博客,错误的地方请严厉指出噢!

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

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

相关文章

教程- VTK.js的基本介绍

VTK.js的核心是标准可视化工具包(VTK)库的JavaScript移植&#xff0c;这是一个c库&#xff0c;旨在促进数据可视化&#xff0c;在此基础上构建了科学可视化应用程序Paraview。VTK.js没有使用OpenGL&#xff0c;而是利用WebGL&#xff0c;主要关注几何和体渲染。因此&#xff0c…

JavaFx程序使用Gloun打包成Android平台App教程

0. 提要 !!! 适合有Maven基础&#xff0c;对JavaFx或JavaFX移动端感兴趣的朋友 提示必须在Linux环境下进行&#xff0c;可以使用虚拟机 推荐使用CentOS系统进行,虚拟机硬盘大小推荐最少给30G 不要像我一样,搞一半又去给文件系统根目录扩大容量 如果容量不够可以看篇博客: http…

C++模板(第二版)笔记之第十八章:模板的多态性

文章目录一、动态多态&#xff08;dynamic polymorphism&#xff09;二、静态多态三、静态多态VS动态多态1.术语2.优点和缺点3.结合两种多态形式&#xff1a;CRTP四、使用concepts五、新形势的设计模式六、泛型编程七、总结一、动态多态&#xff08;dynamic polymorphism&#…

【C语言】内存函数介绍

它们所在的头文件&#xff1a; &#xff08;这里出现的arr都为char类型数组&#xff09;strlen作用&#xff1a;计算一个字符串的长度本质&#xff1a;历经千辛找一个 \0 &#xff0c;找到 \0 就立马停止。&#xff08;就是找 \0 &#xff09;易错&#xff1a;strlen 返回值为 …

物联网无线通信技术中蓝牙和WIFI有哪些区别?

在物联网快速发展的时代&#xff0c;联网运行的设备越来越多&#xff0c;无线通信技术在物联网中发挥着举足轻重的作用&#xff0c;无线通信技术的发展改变了信息传输的方式&#xff0c;人们在任何时间、任何地点都可以访问设备&#xff0c;目前最常用的两种无线通信技术分别是…

云服务器CentOS前后端部署流程记录

部署流程记录 购买云服务ecs服务器&#xff0c;建立CentOS系统 通过xftpxshell访问远程服务 doker部署&#xff08;https://www.runoob.com/docker/centos-docker-install.html&#xff09; docker docker部署环境&#xff08;mysql&#xff09; docker常用命令 1. docker i…

【Linux】进程状态与优先级

文章目录进程状态概念Linux中的进程状态R(running)状态S(sleeping)状态D(disk sleep)状态T(stopped)状态t(tracing stop)状态X(dead)状态Z(zombie)状态特殊的孤儿进程进程优先级进程性质补充进程状态概念 《现代操作系统》中给出的进程状态的定义如下&#xff1a; 进程状态反映…

Qt+C++窗体界面中英文多语言切换

程序示例精选 QtC窗体界面中英文语言切换 如需安装运行环境或远程调试&#xff0c;见文章底部个人微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC窗体界面中英文语言切换>>编写代码&#xff0c;代码整洁&#xff0c;规则&#x…

【Linux】软件包管理器 yum

目录 一、什么是软件包 二、如何进行软件安装 1、yum 的使用 2、yum 配置 一、什么是软件包 在Linux下安装软件&#xff0c;一个通常的办法是下载到程序的源代码&#xff0c;并进行编译&#xff0c;得到可执行程序。但是这样太麻烦了&#xff0c;于是有些人把一些常用的软…

InnoDB数据存储结构

InnoDB数据存储结构 本专栏学习内容来自尚硅谷宋红康老师的视频 有兴趣的小伙伴可以点击视频地址观看 1. 数据库的存储结构&#xff1a;页 索引结构给我们提供了高效的索引方式&#xff0c;不过索引信息以及数据记录都是保存在文件上的&#xff0c;确切来说是存储在页结构中。…

不讨论颜色的前提下,如何证明自己不是色盲?神奇的零知识证明

0x01 一个小故事 《阿里巴巴与四十大盗》中有这样一段小故事&#xff1a; 阿里巴巴会芝麻开门的咒语&#xff0c;强盗向他拷问打开山洞石门的咒语&#xff0c;他不想让人听到咒语&#xff0c;又要向强盗证明他知道这个咒语。 那应该怎么办呢&#xff1f; 便对强盗说&#xf…

基于KVM安装部署RHCOS操作系统

参考&#xff1a;Openshift 4.4 静态 IP 离线安装系列&#xff1a;初始安装 - 米开朗基杨 - 博客园 一、Openshift OCP集群安装架构示意图 RHCOS 的默认用户是 core 如果安装有问题会进入 emergency shell&#xff0c;检查网络、域名解析是否正常&#xff0c;如果正常一般是以…

重修JAVA

程序员的差距是在构思上&#xff1a;思想决定了深度&#xff0c;思想的精髓高深是很多人学不来的&#xff01; 每一门语言都有它的特点&#xff0c;有优势也有劣势&#xff0c; 所以不必拘泥于招式&#xff0c;掌握底层原理即可&#xff01; 每一们语言实际上都是一个“工具”&…

如何在您的香港主机帐户上注册多个域名

注册多个域名非常普遍。事实上&#xff0c;香港主机服务提供商鼓励这样做&#xff0c;因为它既有意义又是必要的。下面将介绍决定为什么您可能需要在香港主机上注册多个域名的几个因素。注册多个域名的原因是什么?方便多个项目如果香港主机帐户的所有者在网络上有多个不同域名…

优化vue项目后, 启动编译项目过程中 报 javaScript heap out of memory 错误 及 nodejs内存溢出

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 1、优化vue项目后&#xff0c;运行npm run serve 启动编译项目过程中 报 javaScript heap out of memory 错误 2、项目启动时&#xff0c;出现 nodejs 内存溢出错误 问题描述 提示&#xff1a;遇到…

分布式事务的背景和解决方案

在常用的关系型数据库&#xff0c;都是具备事务特性的。 那什么是事务呢&#xff1f;事务是数据库运行的一个逻辑工作单元&#xff0c;在这个工作单元内的一系列SQL命令具有原子性操作的特点&#xff0c;也就是说这一系列SQL指令要么全部执行成功&#xff0c;要么全部回滚不执…

经典算法之深度优先搜索(DFS)

&#x1f451;专栏内容&#xff1a;算法学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐。 目录一、前言二、基本概念1.简单介绍2. 官方概念三、动图分析四、模板框架五、例题分析组合问题题干描述&#xff1a;思路…

leetcode146. LRU 缓存【python3哈希表+双向链表】利用OrderedDict以及自实现双向链表

题目&#xff1a; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。实现LRUCache类&#xff1a; LRUCache(int capacity) 以正整数作为容量capacity初始化 LRU 缓存int get(int key) 如果关键字key存在于缓存中&#xff0c;则返回关键字的值&#xff0c;否则…

【论文速递】9位院士Science88页长文:人工智能的进展、挑战与未来

【论文速递】9位院士Science88页长文&#xff1a;人工智能的进展、挑战与未来 【论文原文】&#xff1a;Intelligent Computing: The Latest Advances, Challenges and Future 获取地址&#xff1a;https://spj.science.org/doi/10.34133/icomputing.0006摘要&#xff1a; ​…

【阶段三】Python机器学习15篇:机器学习项目实战:支持向量机回归模型

本篇的思维导图: 项目实战(支持向量机回归模型) 项目背景 股票投资(Stock Investment)是指企业或个人用积累起来的货币购买股票,借以获得收益的行为。股票投资的收益是由“收入收益”和“资本利得”两部分构成的。收入收益是指股票投资者以股东身份,按照持股的份…