C语言内存函数超详解

news2024/12/30 3:04:47

文章目录

  • 前言
  • 1. memcpy
    • 1. 1 memcpy 的使用
    • 1. 2 memcpy 的模拟实现
  • 2. memmove
    • 2. 1 memmove 使用
  • 3. memset
    • 3. 1 memset 函数的使用
    • 3. 2 memset 的模拟实现
  • 4. memcmp
    • 4. 1 memcmp 函数的使用
    • 4. 2 memcmp 的模拟实现


前言

C语言为我们提供了字符串的一些函数,比如复制,比较等等,但是这些函数只能用在字符串上,而C语言的数据类型显然不止字符串一种,那应该怎么办?难道要把每一种数据类型都包装几个函数供我们使用吗?显然这太过臃肿。
为了解决这个问题,C语言在string.h库中提供了内存函数供我们使用。

1. memcpy

void * memcpy ( void * destination, const void * source, size_t num );
  1. 函数memcpysource的位置开始向后复制num字节的数据到destination指向的内存位置。
  2. 这个函数在遇到'\0'的时候并不会停下来。
  3. 如果sourcedestination有任何的重叠,复制的结果都是未定义的。也就是说,sourcedestination后面的num个字节之间不能有重叠的部分

1. 1 memcpy 的使用

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, sizeof(arr1));//sizeof(arr)是40个字节,也就是10个 int 类型的变量
	for (int i = 0; i < 10; i++)
		printf("%d ", arr2[i]);
	return 0;
}

memcpy函数操作的对象是内存中的数据,因此,无论是什么类型的数据,只要大小给的正确,都可以进行复制。

1. 2 memcpy 的模拟实现

在开始前,我们再来看一眼 memcpy 的声明:

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

为什么这里要用 void* 指针?显然是为了让这个函数能接受任何类型的指针,但在函数体内部,这样的指针是不能解引用的,那该怎么办?
我们知道,memcpy函数是以字节为单位复制数据的,而 char 类型的数据的大小就是一个字节,那么我们只需要在函数体里将两个形参都强制类型转换为char* 类型,就可以完成操作了。

void* my_memcpy(void* des, const void* source, size_t num)
{
	//尽管说 memcpy 不能处理重叠的数据,但在模拟实现也不需要管数据是否重叠,因为这是未定义的
	char* cur1 = (char*)des;//当然,你也可以省略这两步,在下面的循环中将两个形参进行临时转换
	char* cur2 = (char*)source;
	while (num--)
		*cur1++ = *cur2++;
	return des;//注意 memcpy 的返回类型是void* ,不是 void ,它返回的是目标内存的起始位置
}

2. memmove

 void * memmove ( void * destination, const void * source, size_t num );
  1. memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2. 1 memmove 使用

既然 memmove 的特点就是能够处理重叠的数据,那我们就让它处理重叠的数据看看效果。

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

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

输出结果:
输出结果
我们来简单的分析一下

memmove(arr+1, arr, 9 * sizeof(int));

这段代码是怎么工作的。
arr+1 是目标位置, arr 是源位置,arr 向后复制 9 个 int类型的大小到arr+1的后面,如果我们按照我们上面模拟实现的 memcpy 的思路去分析,可以发现,arr+1位置的数据,在第一次复制之后,和arr的数据一样了,那么第二次复制时,复制给 arr+2 的数据就也是 arr 的数据了,显然这样不行。
那该怎么办?答案很简单,就是从后往前复制,这样就不会出现上面的问题了。

这样就大功告成了吗?
按照上面的思路,我们来分析一下这个代码:

memmove(arr, arr + 1, 9 * sizeof(int));

如果按照上面的思路从后往前复制,会是什么样的?
第一次复制是将 arr+9 位置的数据复制到 arr+8 ,第二次复制是将 arr+8 的数据复制到 arr+7 ,你看,之前的问题又出现了,arr+7 位置的数据和 arr+9 的数据一样了。
所以应该怎么办?
很简单,分情况:我们发现,当目标空间的起始位置在源空间的起始位置的前面时,我们需要从前往后复制,而目标空间的起始位置在源空间的起始位置的后面时,我们需要从后往前复制,这样就大功告成了。

函数接口:

void* my_memmove(void* dest, const void* source, size_t num)
{
	char* curdest = (char*)dest;
	char* cursou = (char*)source;
	if (dest > source)
	{
		while (num--)
			*(curdest + num) = *(cursou + num);
	}
	else
	{
		while (num--)
			*(curdest++) = *(cursou++);
	}
	return dest;
}

我们再写 main 函数,对两种结果分别进行测试

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

结果1:
结果1
结果2:
结果2
可以看到,无论是哪一种情况,my_memmove 函数都实现了预期的效果。

3. memset

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

3. 1 memset 函数的使用

memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
使用示例:

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

int main()
{
	char str[] = "Hello World.";
	memset(str, 'x', strlen(str) - 2);//strlen是求字符串长度的函数
	printf("%s\n", str);
	return 0;
}

输出结果:
输出结果

看到这里你可能会有疑惑:为什么value是一个int类型的变量却可以接收 char 类型的形参?实际上value在这里接收的是'x'ASCII码值

3. 2 memset 的模拟实现

memcpy 的思路很像,只是没有源位置了,换成了一个 value

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

void* my_memset(void* ptr, int value, size_t num)
{
	char* cur = (char*)ptr;
	while (num--)
		*(cur++) = value;
	return ptr;
}

int main()
{
	char str[] = "Hello World.";
	my_memset(str, 'x', strlen(str) - 2);
	printf("%s\n", str);
	return 0;
}

输出结果同上。

4. memcmp

 int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  1. 比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
  2. 返回值如下:
    返回值
    memcmp 是将一个字节的内容看做 unsigned char 类型进行比较的,其他的和 strcmp 一致。
    图片来源: cplusplus

4. 1 memcmp 函数的使用

使用示例:

#include <stdio.h>
#include <string.h>
int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;
}

输出结果:
输出结果

4. 2 memcmp 的模拟实现

思路基本和 strcmp 一致,只是多了一步强制类型转换,这里不再赘述。

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

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	unsigned char* cur1 = (unsigned char*)ptr1;
	unsigned char* cur2 = (unsigned char*)ptr2;

	while (num--)
	{
		if (*cur1 != *cur2)
			return *cur1 - *cur2;
		cur1++, cur2++;
	}
	return 0;
}

int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n;
	n = my_memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;
}

如果喜欢这篇博客的话不妨顺手点个赞,收藏,评论,关注!
我会持续更新更多优质文章!!

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

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

相关文章

arduino程序-面包板(电路搭建及上传程序控制led))(基础知识)

arduino程序-面包板&#xff08;电路搭建及上传程序控制led&#xff09;&#xff08;基础知识&#xff09; 1-12 面包板&#xff08;电路搭建及上传程序控制led&#xff09;如何使用面包板使用实际元器件搭建电路上传程序到开发板作业 1-12 面包板&#xff08;电路搭建及上传程…

esp-idf-v5.1.1 例程 ble_hid_device_demo 解析

目录 1. 获取ESP-IDF和示例代码 导航到示例代码 3. 示例代码结构 4. 关键文件解析 main.c 初始化和配置: 事件处理: esp_hidd_prf_api.c 和 esp_hidd_prf_api.h esp_hid_gap.c 和 esp_hid_gap.h 5. 编译和烧录 ESP-IDF(Espressif IoT Development Framework)是Es…

批量HEIC转JPG软件推荐:轻松处理大量苹果图片

HEIC格式是苹果设备专属的一种图片格式&#xff0c;它以其卓越的图像质量和高效的压缩能力受到用户的欢迎&#xff0c;但同时也带来了兼容性问题。在很多情况下&#xff0c;我们需要将HEIC格式转换为更为通用的JPG格式&#xff0c;以便在不同设备和平台上进行查看和编辑。对于有…

AttributeError: ‘str‘ object has no attribute ‘decode‘

AttributeError: ‘str‘ object has no attribute ‘decode‘ 目录 AttributeError: ‘str‘ object has no attribute ‘decode‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#x…

老照片AI上色JS开发包【Colorizer.js】

Colorizer.js适用于为人物、建筑、风景等各种老照片自动上色&#xff0c;直接在浏览器内运行&#xff0c; 提供前端JavaScirpt二次开发接口。官方下载地址&#xff1a;Colorizer.js SDK 1、目录组织 Colorizer.js开发包的目录组织说明如下&#xff1a; colorizerjs | - s…

2024电赛H题参考方案(+视频演示+核心控制代码)——自动行驶小车

目录 一、题目要求 二、参考资源获取 三、TI板子可能用到的资源 1、环境搭建及工程移植 2、相关模块的移植 四、控制参考方案 1、整体控制方案视频演示 2、视频演示部分核心代码 五、总结 一、题目要求 小编自认为&#xff1a;此次控制类类型题目的H题&#xff0c;相较于往年较…

【机器学习基础】机器学习的数学基础

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

源码编译安装,及nginx服务控制、监控块

1.源码编译安装&#xff1a; [root17dns ~]# wget https://nginx.org/download/nginx-1.27.0.tar.gz 2.解压&#xff1a; [root17dns ~]# tar -zxvf nginx-1.27.0.tar.gz 3.安装gcc等工具 [root17dns ~]# yum -y install gcc gcc-c [root17dns ~]# yum -y install make lrzsz …

Java的单元测试(@Test)的简单使用

只有Test注解的简单使用&#xff0c;没有其他注解 官网&#xff1a;JUnithttps://junit.org/Test是JUnit框架下的其中一个注解&#xff0c;框架下所有的注解如图所示&#xff0c;但我们现在只说Test的使用方法。 现在我们要在测试类中测试两个对象是否相等&#xff0c;如图所示…

高中数学,集合知识点及题型解答

高中数学&#xff0c;集合知识点及题型解答

《分析模式:可重用对象模型》学习笔记之四:企业财务分析中的观察和测量02

这个模型基本解决问题&#xff0c;可以方便定义层次&#xff0c;以及反映了三个不同的维数元素&#xff0c;也反映了企业部门单元和维数元素的关系&#xff0c;但是很快可以看到&#xff0c;在这里&#xff0c;维数被局限在三个&#xff1a;也就是说&#xff0c;如果维数需要改…

变量在内存中的表现形式

变量的存储在内存中&#xff0c;通常涉及以下几个部分&#xff1a; 栈区&#xff1a;变量名通常存储在栈区&#xff08;空间小&#xff0c;运行速度快&#xff09;&#xff0c;栈区用来存储变量名、函数名、常量名等。当你声明一个变量&#xff08;比如 $a&#xff09;&#x…

OnlyOffice在线部署

部署服务环境&#xff1a;Centos7.6 curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash 安装yum-utils工具 yum install yum-utils 添加nginx.repo源(Nginx官网有最新版&#xff0c;直接copy即可) vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx st…

29.jdk源码阅读之Exchanger

1. 写在前面 Exchanger 是 Java 并发包 (java.util.concurrent) 中的一个同步点工具类&#xff0c;用于在两个线程之间交换数据。它提供了一种简单而强大的方式来实现线程之间的数据交换。不知道大家在日常工作中或者面试中 有遇到它&#xff1f;下面几个问题可以一块来探讨下&…

028-GeoGebra中级篇-脚本的初步的探索

GeoGebra 的脚本功能允许用户通过不同的触发机制&#xff08;如点击、更新、输入框变化、拖动结束&#xff09;和全局 JavaScript 自定义图形和交互行为&#xff0c;实现动态数学模型和用户交互&#xff0c;同时 ggbOnInit() 函数可在应用初始化时设置默认状态&#xff0c;提供…

Git基本原理介绍及常用指令

文章目录 前言一、Git是什么&#xff1f;集中化的版本控制系统分布式版本控制系统 二、Git基本概念三、git命令操作配置用户信息常用指令 总结 前言 如果你用Microsoft Word写过论文&#xff0c;那你一定有这样的经历&#xff1a;想删除一个段落&#xff0c;又怕将来想恢复找不…

MySQL基础练习题8-每月交易1

题目&#xff1a; 查询来查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数及其总金额。 准备数据 分析数据 第一步&#xff1a;用substr()函数来截取到月份&#xff0c;用group by为每个国家分组来查找每个国家 第二步&#xff1a;用count()和sum()来求事务数…

14.FineReport制作带筛选按钮的报表和图表

1.首先连接自带的sqlite数据库&#xff0c;具体方法参考下面的链接 FineReport连接自带的sqlite数据库 2.文件 – 新建普通报表 3.新建数据库查询 4.查询自带的销量表 5.模版&#xff0c;页面设置 6.方向选择横向 7.合并单元格&#xff0c;并添加斜线表头 8.表格中添加字段信…

iPhone手机识别提取藏文字体,推荐《藏语翻译通》藏文OCR识别神器!

如果你正在寻找一款支持藏文OCR识别提取文字的App&#xff0c;我们将向你推荐《藏语翻译通》App&#xff0c;一款专门为iPhone手机用户设计的藏文识别与翻译工具。 功能特点 《藏语翻译通》是一款集藏文OCR识别、藏汉互译、语音识别翻译于一体的应用。用户只需要拿起手机扫一…