【C进阶】内存函数

news2025/1/18 11:41:57

家人们欢迎来到小姜的世界,<<点此>>传送门 这里有详细的关于C/C++/Linux等的解析课程,家人们赶紧冲鸭!!!
客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

内存函数

  • 前言
  • 一、memcpy
    • (一)介绍
    • (二)应用
    • (三)模拟实现
    • (四)进阶:拷贝自己
  • 二、memmove
    • (一)介绍
    • (三)模拟实现
  • 三、memcmp
    • (一)介绍
    • (二)模拟实现
  • 四、memset
    • (一)介绍
    • (二)模拟实现
  • 总结


前言

在之前讲解的关于字符串的拷贝我们已经知道了strcpy,strcmp,strstr等的处理字符串的库函数,但是现在有一个情况了,倘若是数字数组呢?数字字符进行拷贝,移动和比较是不是没有办法,对,这种方法叫做内存函数,所以今天我们要介绍的是memcpy,memmove,memcmp,memset这四个内存函数,对数组是很友好的!


一、memcpy

(一)介绍

在这里插入图片描述
根据图片我们能知道我们是根据字节的大小去决定拷贝过去的数,拷贝的方法是从src拷贝到dest中去。
我们先简单使用一下,如下代码:

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

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

	return 0;
}

在这里插入图片描述

(二)应用

那当然了,如果你想拷贝从3开始往后拷贝4个数(20个字节)的话那我们就直接变值即可,如下代码:

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

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

	return 0;
}

在这里插入图片描述
那我们拷贝17个字节呢?能不能拷贝过去呢?答案是可以的,可是发现下面的数据怎么拷贝了3,4,5,6,7过去呢?因为是数据在计算机中存放的是01 00 00 00小端存放,一直到7的位置存放的是07 00 00 00,所以取到了07,所以7能输出。
在这里插入图片描述

(三)模拟实现

那解释后上完整代码(后面赋代码):
在这里插入图片描述
根据画图我们知道,当我们想拷贝过去的时候,我们并不知道这个指针是什么类型的,是整型?是字符?那不确定我们看着个传参过去的是字节大小,所以我们就想到了强制类型转换成为char*指针一个字节一个字节往后找即可。

#include<stdio.h>
#include<assert.h>
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;
	while (num--) {
		//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
		//一次强转是临时的
		*(char*)dest = *(char*)src;
		//强制类型转换
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		/*++(char*)dest;
		++(char*)src;*/
	}
	return ret;
}

int main() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//不确定arr2和arr1里面是什么类型的指针
	my_memcpy(arr2, arr1 + 2, 17);

	return 0;
}

(四)进阶:拷贝自己

那我们就有个比较大胆的想法,我们能不能实现一下在自身字符串处移动,我们按照原本的套路试一下:

#include<stdio.h>
#include<assert.h>
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;
	while (num--) {
		//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
		//一次强转是临时的
		*(char*)dest = *(char*)src;
		//强制类型转换
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		/*++(char*)dest;
		++(char*)src;*/
	}
	return ret;
}

void test1() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//不确定arr2和arr1里面是什么类型的指针
	my_memcpy(arr2, arr1 + 2, 17);
}

void test2() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr + 2, arr, 20);

}

int main() {
	//test1();
	test2();
	return 0;
}

我们发现test2()函数看的样子很正确,可是真的这样吗?我们进入调试看一看吧!
在这里插入图片描述
这输出结果怎么是1 2 1 2 1 2 1 8 9 10,跟我们的预期结果1 2 1 2 3 4 5 8 9 10怎么差别那么大呢?我们画图解释一下:
在这里插入图片描述
发现当我们想要找替换3的时候,3已经被1覆盖了,只能复制1了,所以是错误的,那这种情况我们需要分情况讨论了:
以下情况是重叠的情况:
情况一:dest(被拷贝的数据)指针在src(源头数据)指针的左边时,需要从左往右拷贝,也就是从前往后拷贝。在这里插入图片描述

情况二:dest(被拷贝的数据)指针在src(源头数据)指针的右边时,需要从右往左拷贝,也就是从后往前拷贝。在这里插入图片描述

不重叠情况:从前往后拷贝和从后往前拷贝都可以。

那这么复杂的情况,早期的计算机编程师肯定想到过,所以就另外制作了一个库函数memmove来解决这种情况,那我们接下来介绍一下memmove库函数吧!

二、memmove

(一)介绍

首先的首先我们,先看一下这个函数在MSDN的介绍:
在这里插入图片描述
有了上面的概念和我们需要进行移动的想法,我们直接用memmove实现一下吧:

在这里插入图片描述
在这里插入图片描述
上面两串代码就是memmove的魅力,不管是从前往后还是从后往前,memmove都可以进行移动。

(三)模拟实现

既然有了上面的思路,那我们模拟实现就很轻松了,只要是dest在src左边的时候,那就是统一从前往后拷贝;dest在src右边的时候,那就是统一从后往前拷贝,注意,这里的dest和src是头指针:

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;

	if (dest < src) {
		//从前向后拷贝
		while (num--) {
			//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
			//一次强转是临时的
			*(char*)dest = *(char*)src;
			//强制类型转换
			dest = (char*)dest + 1;
			src = (char*)src + 1;
			/*++(char*)dest;
			++(char*)src;*/
		}
	}
	else {
		//从后向前拷贝
		while (num--) {
			//第一次往后跳了19个字节
			*((char*)dest + num) = *((char*)src + num);

		}
	}
	return ret;
}
void test3() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr, arr + 2, 20);

}

int main() {
	//test1();
	//test2();
	test3();
	return 0;
}

三、memcmp

(一)介绍

我们先看一下MSDN的介绍:
在这里插入图片描述
即;比较从ptr1和ptr2指针开始的num个字节。
那我们根据介绍进行制作一下这个吧!

在这里插入图片描述
如图所示,当我们在进行比较的时候,是根据字节大小比较的,所以这就取决于编译器是大端还是小端,根据字节在内存中的存放顺序进行比较的,我们知道,在VS环境下,是小端存储,也就是说5是0x05 00 00 00,03是0x03 00 00 00,所以取第一个字节,较大的是05,所以当字节数为不是4的倍数的时候,我们也可以比较:
在这里插入图片描述

(二)模拟实现

#include<stdio.h>
#include<assert>
int my_memcmp(const void* buf1, const void* buf2, size_t count) 
{
	assert(buf1 && buf2);
	//往后找字节进行比较
	while (count--) {
		if (*(char*)buf1 > *(char*)buf2) {
			return 1;
		}
		else if (*(char*)buf1 < *(char*)buf2) {
			return -1;
		}
		buf1 = (char*)buf1 + 1;//往后移动
		buf2 = (char*)buf2 + 1;
	}
	return 0;
}

int main() {
	int arr1[] = { 1,2,3 };
	int arr2[] = { 1,2,5 };
	int ret = my_memcmp(arr1, arr2, 9);
	printf("%d\n", ret);
	return 0;
}

四、memset

(一)介绍

名为内存设置函数,是以字节为单位来设置内存中的数据的。
在这里插入图片描述
如下所示,根据MSDN介绍我们进行书写:
在这里插入图片描述

(二)模拟实现

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

void* my_memset(void* dest, int c, size_t count)
{
	assert(dest);
	//头指针给存起来
	void* start = dest;
	//循环相赋值
	while (count--) {
		*(char*)dest = (char)c;
		dest = (char*)dest + 1;
	}
	return start;
}

int main()
{
	char arr[] = "hello world!";
	char* ret = my_memset(arr, 'x', 5);
	printf("%s\n", arr);

	return 0;
}

总结

当我们使用这些函数的时候,我们需要搞清楚这些函数的传参以及我们需要注意的事情,尤其是我们需要熟悉并掌握模拟实现这些内存函数的使用,这个是很关键的,对于自己是有很大的提升的!!!


客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

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

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

相关文章

Spring知识点记录

Spring知识点1. Spring简介&#xff08;此章略过&#xff09;1.1 Spring概述1.2 Spring家族1.3 Spring Framework2. IOC2.1 IOC容器2.2 基于XML管理bean2.2.1 入门案例&#xff08;ioc容器的整体思路&#xff09;2.2.2 获取bean的三种方式2.2.3 依赖注入2.2.3.1 setter注入2.2.…

指针数组与数组指针---傻傻分不清楚?

目录1.指针数组2.数组指针1.指针数组 什么是指针数组呢&#xff1f; 我们通过以前学过的数组进行类比&#xff1a; 整形数组是存放整形的数组字符数组是存放字符的数组所以&#xff0c;指针数组是存放指针(地址)的数组 整形数组的书写形式是int arr[10]&#xff0c;字符指针…

uniapp兼容小程序和H5遇见的坑

目录 一、报错 vue service.requestcomponentinfo:typeerror:cannot read property matches undefined H5不兼容uni-app的image标签 小程序externalClasses不兼容h5 H5不兼容van-picker和van-datetime-picker 一、报错 vue service.requestcomponentinfo:typeerror:cannot r…

【C语言进阶】 一篇带你掌握字符串和内存函数

目录一&#xff1a;strlen模拟实现&#xff1a;二&#xff1a;strcpy模拟实现&#xff1a;三&#xff1a;strcat模拟实现&#xff1a;四&#xff1a;strcmp模拟实现&#xff1a;五&#xff1a;strncpy模拟实现&#xff1a;六&#xff1a;strncat模拟实现&#xff1a;七&#xf…

如何正确地连接PLC与7种设备的输入输出线路

正确地连接输入和输出线路&#xff0c;是保证PLC可靠工作的前提。想要正确地连接PLC与7种设备的输入输出线路&#xff0c;就需注意以下几个方面&#xff1a;1.PLC与主令电器类设备的连接下图是PLC与按钮、行程开关、转换开关等主令电器类输入设备的接线示意图。图中的PLC为直流…

3、运算符

目录 一、算数运算符 二、关系运算符 三、逻辑运算符 一、算数运算符 MATLAB中的算术运算符有加、减、乘、除、点乘、点除等&#xff0c;其运算法则表&#xff1a; 示例1&#xff1a;数值与矩阵的算术运算 Aeye(2)Bones(2)CA*BDA.*B 运行结果&#xff1a; 此外&#xff0…

25.数组指针取*,指针和函数的关系,指针作为函数的参数

数组指针取* 数组指针取*&#xff0c;并不是取值的意思&#xff0c;而是指针的类型发生变化&#xff1b; 一维数组指针取*&#xff0c;结果为它指向的一维数组第0个元素的地址&#xff0c;它们还是指向同一个地方。二维数组指针取*&#xff0c;结果为一维数组指针&#xff0c…

Excel实战 第1章 数据处理

目录1 数据规范2. 导入数据3. 数据清洗3.1 重复数据处理3.2 缺失数据处理3.3 空格数据处理1 数据规范 2. 导入数据 3. 数据清洗 3.1 重复数据处理 数据透视表法&#xff08;首选&#xff09; 选中A、B两列&#xff0c;点击【插入】–》【数据透视表】 把号码拉到【行】&…

LeetCode 207. 课程表

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 207. 课程表&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCode 207. …

图表控件LightningChart.NET 系列教程(六):许可证管理介绍(下)

LightningChart.NET SDK 是一款高性能数据可视化插件工具&#xff0c;由数据可视化软件组件和工具类组成&#xff0c;可支持基于 Windows 的用户界面框架&#xff08;Windows Presentation Foundation&#xff09;、Windows 通用应用平台&#xff08;Universal Windows Platfor…

Docker面试题(史上最全 + 持续更新)

专题37&#xff1a;Docker面试题&#xff08;史上最全、定期更新&#xff09; 本文版本说明&#xff1a;V26 《尼恩面试宝典》升级的规划为&#xff1a; 后续基本上&#xff0c;每一个月&#xff0c;都会发布一次&#xff0c;最新版本&#xff0c;可以联系构师尼恩获取&…

VALL-E:Neural Codec Language Models are Zero-Shot Text to Speech Synthesizers

文章目录NLP中Bert的进展历史word embeddingELMOGPTBertAudioLM: a Language Modeling Approach to Audio Generationabstractintrorelated workVALL-E:Neural Codec Language Models are Zero-Shot Text to Speech Synthesizersabstractspeech quantizationtraining:condition…

程序的机器级表示part2——访问信息

目录 1. 整数寄存器 2. 操作数指示符与寻址模式 3. 数据传送指令 4. 压入和弹出栈数据 1. 整数寄存器 一个x86-64的CPU包含一组16个存储64位值的通用目的寄存器(general-purpose registers) 整型寄存器x86-64的CPU是64位的&#xff0c;因此相应的寄存器长度与机器字长(数据…

基于matlab实现的水果识别系统+源码+代码注释

水果识别 摘要&#xff1a; 本项目针对多种常见水果混合的图像&#xff0c;利用 Matlab 软件&#xff0c;对水果的识别进行研究。根据水果和背景的差别选取阈值&#xff0c;对去噪增强对比度后的图像进行二值化处理。再对图像进行边缘检测&#xff0c;选定连通区域&#xff0…

仅需一个注解,实现springboot项目中的隐私数据脱敏

文章参考&#xff1a; 仅是为了方便查看&#xff0c;因此在此记录一下​​​​​​​ 目录 1. 创建隐私数据类型枚举&#xff1a;PrivacyTypeEnum 2. 创建自定义隐私注解&#xff1a;PrivacyEncrypt 3. 创建自定义序列化器&#xff1a;PrivacySerializer 4. 隐私数据隐藏工…

高德地图开发之地图配置及vue上初始化创建地图

开门见山地说&#xff0c;现如今地图相关的开发业务在工作中越来越常见&#xff0c;目前市面上主流的地图开发还是以百度和高德为主&#xff0c;今天就来讲一下高德地图的配置和在vue上的初始化创建&#xff0c;帮助大家更好更快的了解高德地图开发相关的内容。 一. 注册地图 …

Python协程的四种实现方式

今天继续给大家介绍Python关知识&#xff0c;本文主要内容是Python协程的四种实现方式。 一、yield关键字实现方式 以yield关键字方式实现协程代码如下所示&#xff1a; def fun1():yield 1yield from fun2()yield 2def fun2():yield 3yield 4f1fun1() for item in f1:print…

steam搬砖项目靠谱吗?怎么样?

Steam搬砖’通俗来讲&#xff1a;小白都可以轻松上手&#xff0c;不需要会玩游戏&#xff0c;项目就是利用低价美金充值卡&#xff0c;从国外Steam平台购买CS:GO游戏装备回来&#xff0c;在国内网易BUFF平台售卖&#xff0c;低买高卖&#xff0c;产生利润的一个项目 首先&…

javascript实现版本号自增打包vue写入版本号

文章目录Preface代码方法一&#xff1a;兼容多长版本号。如&#xff1a;1.0.0.0.0.0方法二&#xff1a;只针对3位版本号打包时写入版本号Preface 前段时间出差去帮忙别的项目&#xff0c;项目中有前端更新提醒用户更新。 更新提示是放在了axios请求中。 这个需求是别人做的&…

再学C语言34:数组

数组&#xff08;array&#xff09;由一系列类型相同的元素构成 数组的声明&#xff1a;包括数组元素的数目和元素的类型&#xff0c;编译器根据这些信息创建合数的数组&#xff0c;数组元素可以具有同普通变量一样的类型&#xff1a; int main(void) {int a[5]; // 包含5…