【C语言基础】:内存操作函数

news2024/11/13 8:16:12

文章目录

      • 一、memcpy函数的使用和模拟实现
        • 1.1 memcpy函数的使用
        • 1.2 memcpy函数的模拟实现
      • 二、memmove函数的使用和模拟实现
        • 2.1 memmove函数的使用
        • 2.2 memmove函数的模拟实现
      • 三、memset函数的使用
        • 3.1 menset函数的使用
      • 四、memcmp函数的使用
        • 4.1 memcmp函数的使用

在这里插入图片描述
                                                 学海无涯苦作海
创作不易,宝子们!如果这篇文章对你们有帮助的话,别忘了给个免费的赞哟~

一、memcpy函数的使用和模拟实现

函数原型

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

内存复制块

  1. 将num字节的值从源指向的位置直接复制到目标指向的内存块。
  2. 源指针和目标指针所指向的对象的底层类型与此函数无关;结果是数据的二进制副本。
  3. 该函数不检查源中是否有任何终止null字符——它总是精确地复制num个字节。
  4. 为了避免溢出,目标参数和源参数所指向的数组的大小应该至少为num字节,并且不应该重叠(对于重叠的内存块,memmove是一种更安全的方法)。
1.1 memcpy函数的使用

【示例】:将arr1中的前5个元素拷贝到arr2中

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

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

在这里插入图片描述

1.2 memcpy函数的模拟实现

模拟思路

函数参数和返回类型mencpy不仅可以将拷贝整形数据,其他的数据类型也可以拷贝,即参数类型根据传入的数据类型决定,需要接受任意类型的地址,所以参数类型可以定义成void*,因为src是原数据,我们不期望它被修改,所以要加const进行修饰;还需要指定拷贝num字节的值,即num要为非负数,为了避免传入进来的num是一个负数,可以将num定为size_t类型,memcpy返回的是目标空间的起始地址,即返回类型我们也定为void* 。
函数体:首先用assert断言判断传入进来的是否为空指针;void* 类型的指针不可以直接解引用,这里的num是指字节数,不同的数据类型所占的字节数不同,所以最好一个一个字节访问,即将 void* 强转成 char* (解引用时一次访问一个字节)类型;每访问一个字节后就自增一,拷贝完成后,这时的dest已经不再指向首元素地址,所以在这之前要创建一个void* 的指针记录dest,最后返回记录dest的指针即可。

模拟代码

#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--)
	{
		*(char*)dest = *(char*)src;
		src = (char*)src + 1;
		dest = (char*)dest + 1;
	}
	return ret;
}

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

在这里插入图片描述
注意memcpy函数不可以拷贝重叠的内存块(虽然也能实现)但不安全,对于重叠的内存块,memmove是一种更安全的方法。

二、memmove函数的使用和模拟实现

函数原型

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

移动内存块

  1. 将num字节的值从源指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样进行,从而允许目标和源重叠。
  2. 源指针和目标指针所指向的对象的底层类型与此函数无关;结果是数据的二进制副本。
  3. 该函数不检查源中是否有任何终止null字符——它总是精确地复制num个字节。
  4. 为了避免溢出,目的参数和源参数所指向的数组的大小至少为num字节。

memcpymemmove的区别就在于memmove可以复制重叠的内存块

2.1 memmove函数的使用

【示例】:将arr1中的1,2,3,4,5这几个元素拷贝到arr1中的3,4,5,6,7的位置。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述

2.2 memmove函数的模拟实现

在这里插入图片描述

模拟思路

函数参数和返回类型:和memcpy函数一致,这里就不多作介绍了。
函数体:这里总体要分两种情况:一种是dest<src,另一种就是dest>src;对于第一种情况,需要从前向后拷贝,对于第二种情况,需要从后向前拷贝,至于不重叠的话,无论是从前向后还是从后向前都是可以的。从前向后拷贝的方法和memcpy是一致的,这里就不多说了;从后向前拷贝也比较简单,src强转成 char* 之后加上num就是最后一个字节,然后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 (dest < src)
	{
		// 前-->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			src = (char*)src + 1;
			dest = (char*)dest + 1;
		}
	}
	else {
		// 后-->前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

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

在这里插入图片描述

三、memset函数的使用

函数原型

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

填充内存块
将ptr指向的内存块的前num个字节设置为指定的值(解释为unsigned char)。
参数说明
ptr
指向要填充的内存块的指针。
value
需要设置的值。该值作为int类型传递,但函数使用该值的unsigned char转换来填充内存块。
num
要设置为该值的字节数。size_t是一个无符号整型。

3.1 menset函数的使用

【示例】:将arr数组中的hello替换成x

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 5);
	printf("%s\n", arr);
	return 0;
}

在这里插入图片描述
注意:这里的num指的是该值的字节数

四、memcmp函数的使用

函数原型

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

比较两个内存块
将ptr1所指向的内存块的前num字节与ptr2所指向的前num字节进行比较,如果它们都匹配则返回0,如果不匹配则返回不同于0的值,表示哪个值更大。
参数说明
ptr1
指向内存块的指针。
ptr2
指向内存块的指针。
num
要比较的字节数。
注意:与strcmp不同,该函数在找到空字符后不会停止比较。

return value(返回值)indicates(含义)
<0在两个内存块中不匹配的第一个字节在ptr1中的值低于ptr2中的值(如果作为unsigned char值计算)
0两个内存块的内容是相等的
.>0在两个内存块中不匹配的第一个字节在ptr1中的值大于ptr2中的值(如果作为unsigned char值计算)
4.1 memcmp函数的使用

【示例】:比较arr1数组和arr2数组中前16个字节;在比较前17个字节。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[] = { 1,2,3,4,8,8,8 };
	int ret1 = memcmp(arr1, arr2, 16);
	int ret2 = memcmp(arr1, arr2, 17);
	printf("%d\n", ret1);
	printf("%d\n", ret2);
	return 0;
}

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

分析:一个整型占4个字节,比较前16个字节就是比较前4个元素,前4个元素都是一样的,所以返回0。但从17个字节开始,arr1和arr2数组就开始不同了,arr1是05,而arr2是08,08大于05,所以返回-1。

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

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

相关文章

Qt与编码

ASCII码:一个字节&#xff0c;256个字符。 Unicode:字母&#xff0c;汉字都占用两个字节。 utf-8:字母一个字节&#xff0c;汉字3个字节。 gbk:字母一个字节&#xff0c;汉字2个字节。 gb2312:可以表示汉字&#xff0c;gb2312<gbk。 编码查看&#xff1a; https://www.…

钡铼技术R40路由器助力构建无人值守的智能化污水处理厂

钡铼技术R40路由器作为智能化污水处理厂的关键网络设备&#xff0c;发挥着至关重要的作用&#xff0c;助力构建无人值守的智能化污水处理系统。在现代社会&#xff0c;污水处理是城市环境保护和可持续发展的重要组成部分&#xff0c;而智能化污水处理厂借助先进的技术和设备&am…

C语言数据结构易错知识点(5)(插入排序、选择排序)

插入排序&#xff1a;直接插入排序、希尔排序 选择排序&#xff1a;直接选择排序、堆排序 上述排序都是需要掌握的&#xff0c;但原理不会讲解&#xff0c;网上有很多详尽地解释&#xff0c;本文章主要分享一下代码实现上应当注意的事项 1.直接插入排序&#xff1a; 代码实…

Kevin的128纪念日

上面这个是我在三天前做的一个开场白一样的封面。在设计的时候我的想法很简单&#xff0c;把自己给展现出来。我没有去过多的加其他花花绿绿的东西&#xff0c;我想把我本身的状态和形象给凸显出来。 哈哈~看到这里有人就想问&#xff0c;这个躺在沙发上吃零食的懒猫就是你的个…

利用瑞士军刀netcat建立连接并实现文件上传

实验环境&#xff1a; Kali:192.168.117.129 Windows10:192.168.135.142 第一步&#xff1a;建立连接 在Windows上下载netcat(官网搜索) 下载好之后在netcat目录打开cmd进入小黑屏 实验一&#xff1a;建立虚拟机与主机的连接 命令&#xff1a; Kali:nc 192.168.135.144…

FastAPI+React全栈开发05 React前端框架概述

Chapter01 Web Development and the FARM Stack 05 The frontend React FastAPIReact全栈开发05 React前端框架概述 Let’s start with a bit of context here. Perhaps the changes in the world of the web are most visible when we talk about the frontend, the part o…

前端学习之css基本网格布局

网格布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>网格布局</title><style>.a{/* grid网格布局 */display: grid;width: 400px;height: 400px;border: 1px solid red;/* 设置当前…

【Spring】IoCDI详解

1. IoC详解 前面提到过IoC就是将对象的控制权交由Spring的IoC容器进行管理&#xff0c;由Spring的IoC容器创建和销毁bean&#xff0c;那么既然涉及到容器&#xff0c;就一定包含以下两方面功能&#xff1a; bean的存储bean的获取 1.1 类注解 Spring框架为了更好地服务应用程…

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能 文章目录 RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能1. RabbitMQ端口说明2. 开启Rabbitmq后台管理功能1. 查看rabbitmq已安装的插件2. 开启rabbitmq后台管理平台插件3. 开启插件后&#xff0c;再次查看插…

学习笔记Day15:Shell脚本编程

Shell脚本编程 Linux系统环境 Linux系统的4个主要部分&#xff1a;内核、shell、文件系统和应用程序。 内核是操作系统的核心&#xff0c;决定系统性能和稳定性shell &#xff1a;一种应用程序&#xff0c;是用户和内核交互操作的接口&#xff0c;是套在内核外的壳&#xff…

Linux文件IO(2):使用标准IO进行文件的打开、关闭、读写、流定位等相关操作

目录 前言 文件的打开和关闭的概念 文件的打开 文件的打开函数 文件打开的模式 文件的关闭 文件的关闭函数 注意事项 字符的输入&#xff08;读单个字符&#xff09; 字符输入的函数 注意事项 字符的输出&#xff08;写单个字符&#xff09; 字符输出的函数 注意…

实战|使用 Node.js 和 htmx 构建全栈应用程序

在本教程中&#xff0c;我将演示如何使用 Node 作为后端和 htmx 作为前端来构建功能齐全的 CRUD 应用程序。这将演示 htmx 如何集成到全栈应用程序中&#xff0c;使您能够评估其有效性并确定它是否是您未来项目的不错选择。 htmx 是一个现代 JavaScript 库&#xff0c;旨在通过…

微服务day07 -- 搜索引擎 ( 数据聚合 + 自动补全 + 数据同步 + ES集群 )

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

ydata_profiling:自动生成数据探索报告的Python库

之前在做数据分析的时候&#xff0c;用过一个自动化生成数据探索报告的Python库&#xff1a;ydata_profiling 一般我们在做数据处理前会进行数据探索&#xff0c;包括看统计分布、可视化图表、数据质量情况等&#xff0c;这个过程会消耗很多时间&#xff0c;可能需要上百行代码…

数据库实验(一)SQL Server触发器

目录 触发器的定义 触发器和存储过程的区别 触发器的优点 触发器的作用 触发器的分类 DML触发器 DDL触发器 登录触发器 触发器的工作原理 inserted表 deleted表 创建触发器 编程要求 测试要求&#xff1a; 实验代码&#xff1a; 触发器的定义 触发器是建立在触…

视频推拉流EasyDSS点播平台云端录像播放异常的问题排查与解决

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户反馈&#xff0c;项目现…

深度学习基础知识

本文内容来自https://zhuanlan.zhihu.com/p/106763782 此文章是用于学习上述链接&#xff0c;方便自己理解的笔记 1.深度学习的网络结构 深度学习是神经网络的一种特殊形式&#xff0c;典型的神经网络如下图所示。 神经元&#xff1a;表示输入、中间数值、输出数值点。例如&…

itextPdf生成pdf简单示例

文章环境 jdk1.8&#xff0c;springboot2.6.13 POM依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.ite…

【前端面试3+1】01闭包、跨域

一、对闭包的理解 定义&#xff1a; 闭包是指在一个函数内部定义的函数&#xff0c;并且该内部函数可以访问外部函数的变量。闭包使得函数内部的变量在函数执行完后仍然可以被访问和操作。 特点&#xff1a; 闭包可以访问外部函数的变量&#xff0c;即使外部函数已经执行完毕。…

【编译tingsboard】出现gradle-maven-plugin:1.0.11:invoke (default)

出现的错误&#xff1a; [ERROR] Failed to execute goal org.thingsboard:gradle-maven-plugin:1.0.11:invoke (default) on project http: Execution default of goal org.thingsboard:gradle-maven-plugin:1.0.11:invoke failed: Plugin org.thingsboard:gradle-maven-plugi…