详解C语言string.h中常用的14个库函数(四)

news2024/11/15 13:00:43

本篇博客会讲解最后4个函数,分别是memset, memcpy, memmove, memcmp。这4个函数开头都是mem,即memory(内存)的缩写。

string.h中的库函数

memset

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

memset可以用来设置内存中的值。该函数可以把从ptr指向的空间开始,后面的num个字节设置成value的值。

举个简单的例子。假设有一个数组:

int arr[] = { 1,2,3,4,5 };

我们想把这个数组的前3个数都设置成0,就这么写:

memset(arr, 0, 3 * sizeof(int));

打开内存窗口观察一下,这是设置前:

在这里插入图片描述

这是设置后:
在这里插入图片描述
可以看到,成功把前3个数设置成了0。

memcpy

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

memcpy是用来拷贝内存中的数据的。拷贝的起始位置由第2个参数决定,目标位置由第1个参数决定,注意:目标在前,源头在后,后面的memmove同理。总共拷贝多少个字节的数据,由第3个参数决定。函数会返回目标空间的起始地址。

比如,假设有2个数组:

int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 0 };

如果我想把arr1的数据都拷贝到arr2中,就应该这么写:

memcpy(arr2, arr1, sizeof(arr1));

拷贝完后,把arr2中的数据打印出来,就可以看到拷贝成功了。
在这里插入图片描述
这个函数和接下来会讲解的memmove函数都是非常重要的,所以我模拟实现一下。模拟实现的思路很简单,每次从源头拷贝到目的地,拷贝num次即可。

void* my_memcpy(void* dst, const void* src, size_t num)
{
	assert(dst && src);
	void* ret = dst;

	while (num--)
	{
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}

	return ret;
}

根据以上的实现,我们可以发现,使用memcpy进行内存拷贝时,源头和目的地的空间是不能重叠的。加入重叠了,比如:

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

以上程序中,数组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 6 7 8 9 10]。假设把拷贝后的arr数组打印出来,结果如下:
在这里插入图片描述
可以看到并不符合预期。原因是,由于源头和目的地有重叠,在拷贝左边的数据时,其实已经把源头给覆盖了。

事实上,如果使用库里的memcpy来拷贝,源头和目的地有重叠时,结果是标准未定义的。此时不能使用memcpy,而应使用memmove。

memmove

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

memmove和memcpy的使用方式完全相同。唯一的区别是,使用memmove来拷贝时,源头和目的地是可以重叠的。

这里我们来模拟实现一下这个函数。假设源头和目的地没有重叠,前面memcpy的实现是没有任何问题的,但是一旦有重叠时,需要分类讨论。

  1. src<dst,此时源头在目的地的左边,应该先拷贝右边的数据,再拷贝左边的数据。
  2. dst<src,此时源头在目的地的右边,应该先拷贝左边的数据,再拷贝右边的数据。

以第1点为例,第2点同理。假设有一个数组,内存分布(左边是低地址)是:[1 2 3 4 5 6 7 8 9 10]。假设想把[1 2 3 4 5]所在位置的数据拷贝到[3 4 5 6 7]所在的位置,假设先拷贝左边的数据,再拷贝右边的数据。一上来先把1拷贝到3所在的位置,得:[1 2 1 4 5 6 7 8 9 10],再拷贝2:[1 2 1 2 5 6 7 8 9 10],此时我们应该拷贝3,但是3呢?不见了,已经被覆盖了。所以在这种情况下,先拷贝左边的数据是不对的,应该先拷贝右边的数据。

分类讨论后,实现如下:

void* my_memmove(void* dst, const void* src, size_t num)
{
	assert(dst && src);
	void* ret = dst;

	if (dst < src)
	{
		// 左->右
		while (num--)
		{
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		// 右->左
		while (num--)
		{
			*((char*)dst + num) = *((char*)src + num);
		}
	}

	return ret;
}

此时再运行:

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

把arr打印出来,如下:
在这里插入图片描述
而如果是这样:

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

数组arr打印出来的结果如下:
在这里插入图片描述

memcmp

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

memcmp是用来比较内存中的值的,它会一个字节一个字节向后比较,直到遇到某一对数据比较出大小,或者所有num个字节的数据都相等。

返回值和strcmp类似,如果左边>右边返回正数,左边<右边返回负数,左边==右边返回0。

比如,在小端机器下,运行以下程序:

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

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 4 * sizeof(int) + 1);
	if (ret > 0)
	{
		printf(">\n");
	}
	else if (ret < 0)
	{
		printf("<\n");
	}
	else
	{
		printf("==\n");
	}

	return 0;
}

得到的结果是“小于”。因为arr1的内存分布是(左边是低地址,字节和字节之间用空格分隔):[01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00],arr2的内存分布是:[01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00],一直比较,直到05<06,得到“小于”。如果把memcmp的第3个参数改成4*sizeof(int),结果将是“等于”。

总结

  1. 本篇博客主要讲解了4个内存操作函数。
  2. memset是用来设置内存中的值的。
  3. memcpy和memmove都可以用来拷贝内存中的值,如果源头和目的地空间有重叠,则必须用memmove。
  4. memcmp是用来比较内存中的值的大小的,返回值和strcmp类似,但是memcmp是比较出大小或者比较完num个字节就结束,而strcmp是比较出大小或者比较到\0就结束。

感谢大家的阅读!

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

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

相关文章

深度学习实战——循环神经网络(RNN、LSTM、GRU)

忆如完整项目/代码详见github&#xff1a;https://github.com/yiru1225&#xff08;转载标明出处 勿白嫖 star for projects thanks&#xff09; 目录 系列文章目录 一、实验综述 1.实验工具及内容 2.实验数据 3.实验目标 4.实验步骤 二、循环神经网络综述 1.循环神经…

【数据结构】第五章 树与二叉树

文章目录 知识体系5.1 树的基本概念5.1.1 树的定义5.1.2 基本术语5.1.3 树的性质 5.2 二叉树的概念5.2.1 二叉树的定义和主要特性5.2.2 二叉树的存储结构 5.3 二叉树的遍历和线索二叉树5.3.1 二叉树的遍历5.3.2 线索二叉树 5.4 树、森林5.4.1 树的存储结构5.4.2 树、森林与二叉…

uniapp踩坑之项目:各端条件编译

在 HBuilderX 中&#xff0c;ctrlalt/ 即可生成正确注释&#xff08;js&#xff1a;// 注释、css&#xff1a;/* 注释 */、vue/nvue模板&#xff1a; &#xff09;。 #ifdef&#xff1a;if defined 仅在某平台存在#ifndef&#xff1a;if not defined 除了某平台均存在%PLATFORM…

ARM busybox 的移植实战2

一、busybox 源码分析1 1、源码目录梳理 2、整个程序入口的确认 (1) 分析一个程序&#xff0c;不管多庞大还是小&#xff0c;最好的路线都是 按照程序运行时的逻辑顺序来。所以找到一个程序的入口至关重要。 (2) 学 C 语言的时候都知道&#xff0c;程序的主函数 main 函数就是…

机器学习算法 随机森林

文章目录 一、概述1.1 集成学习1.2 决策树1.3 随机森林 二、Sklearn中的随机森林2.1 分类树API2.2 参数 2.2 回归树API2.2.1 重要参数 2.3 随机森林调参 三、总结 一、概述 1.1 集成学习 多个模型集成成为的模型叫做集成评估器&#xff08;ensemble estimator&#xff09;&am…

车载软件架构——闲聊几句AUTOSAR BSW(二)

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 我特别喜欢一个老话,来都来了。我觉得这就是松弛感,既然来了,就开心起来吧!松弛感来自于专注,焦虑不是靠克服的,是靠忘记的,当你很专注做一件事的时候…

HNCTF-re部分复现

目录 [HNCTF 2022 WEEK3]Help_Me! [HNCTF 2022 WEEK3]Whats 1n DLL? [HNCTF 2022 WEEK4]ez_maze 这几天在做HNCTF的week3&#xff0c;week4部分&#xff0c;学到了一些不知道的没接触过的东西&#xff0c;所以记录一下 [HNCTF 2022 WEEK3]Help_Me! 题目下载&#xff1a;下…

onnx笔记2:onnx操作实例

1. 介绍 本文以yolov5s模型,演示对yolov5s.onnx模型文件的读取,修改等操作 2. onnx操作 2.1 获取数据 (1) 案例1 :读取weights数据 比如获取yolov5s.onnx第一个Conv的weights数据。 点击左侧第一个Conv, 右侧INPUTS下面的W点开+号,可以看到该Conv的weight的name为m…

MySQL --- 主从复制、读写分离

一、MySQL主从复制 MySQL数据库默认是支持主从复制的&#xff0c;不需要借助于其他的技术&#xff0c;我们只需要在数据库中简单的配置即可。接下来&#xff0c;我们就从以下的几个方面&#xff0c;来介绍一下主从复制 1.1、介绍 MySQL主从复制是一个异步的复制过程&#xff0c…

linux 安装 oracle 11g

linux 安装 oracle 11g 1、下载oracle 11g (11.2.0.1.0)1.1、Oracle Database 11.2.0.1.01.2、Oracle Database Grid Infrastructure 11.2.0.1.01.3、客户端 2、安装文档3、安装前准备3.1、建立用户和用户组3.2、sysctl3.3、security limits3.4、其他设置3.5、创建安装目录3.6、…

SpringBootWeb请求响应

目录 前言 1. 请求 1.1 Postman 1.1.1 介绍 1.1.2 安装 1.2 简单参数 1.2.1 原始方式 1.2.2 SpringBoot方式 1.2.3 参数名不一致 小结 1.3 实体参数 1.3.1 简单实体对象 1.3.2 复杂实体对象 1.4 数组集合参数 1.4.1 数组 1.4.2 集合 1.5 日期参数 1.6 JSON参…

液压轴位置闭环控制(比例伺服阀应用)

液压阀的基础知识请参看下面的博客文章: PLC液压控制系列之比例流量阀结构分析_RXXW_Dor的博客-CSDN博客比例流量阀液压同步控制的PID闭环调节可以参看下面这篇博文三菱FX3U-PLC 前馈+PID闭环调节实现液压同步控制(比例换向阀)_RXXW_Dor的博客-CSDN博客液压控制系统在工业现…

plt got

小白垃圾笔记而已&#xff0c;不建议阅读。 本来是仅仅写的文字&#xff0c;因为我并没有调试出来&#xff0c;在群里问了师傅后才知道是因为我开起了 full Pelro保护。 按理说应该关闭的&#xff0c;或者开启部分也可以。gcc -z lazy -o test test.c // 部分开启, 即…

跨平台跨端的登录流程及其安全设计

跨平台跨端的登录流程及其安全设计 目录 跨平台跨端的登录流程及其安全设计 一、登录流程 1.1、登录流程时序图 1.2、三方App 登录 1.3、请求的路由守卫 二、注册流程 2.1、注册流程时序图 2.2、多因素认证 2.3、自动跳转登录页面 三、涉及的技术与安全 3.1、用户…

基于深度学习的图片上色(Opencv,Pytorch,CNN)

文章目录 1. 前言2.图像格式&#xff08;RGB&#xff0c;HSV&#xff0c;Lab&#xff09;2.1 RGB2.2 hsv2.3 Lab 3. 生成对抗网络&#xff08;GAN&#xff09;3.1 生成网络&#xff08;Unet&#xff09;3.2 判别网络&#xff08;resnet18&#xff09; 4. 数据集5. 模型训练与预…

OSCP-Medjed(重置用户密码、mysql写webshell、可写文件替换提权)

目录 扫描 FTP WEB 提权 扫描 FTP 尝试登录到FTP服务器,该服务器位于端口30021 使用Filezilla,并能够浏览文件。那里有一些配置文件,但找不到任何值得注意的东西,不能写入目录。

成长之路---C语言笔记(构造类型之字符数组及字符串函数)

决不要停止自学&#xff0c;也不要忘记&#xff0c;不管你已经学到了多少东西&#xff0c;已经知道了多少东西&#xff0c;知识和学问是没有止境的一鲁巴金 字符数组 字符数组就是用于存放字符型数据的数组。在C语言中&#xff0c;字符串是作为字符数组来处理的&#xff0c;没有…

redis设计与实现读书笔记(2)

今天看的是关于单机数据库&#xff0c;RDB持久化以及AOF持久化的内容。 关于单机数据库 1.默认数据库数量 redis的服务器默认是会创建16个数据库&#xff0c;每个客户端访问的时候都要指定自己的目标数据库。 select可以切换目标数据库。 注意事项 到目前为止&#xff0c…

部署YUM仓库

文章目录 1. YUM仓库服务1.1 YUM概述1.1 准备安装源 2.制作YUM源2.1制作ftp源2.2 国内在线yum源2.3 本地源与在线源同时使用 3.yum软件包的下载方式4.yum的常用操作命令 1. YUM仓库服务 1.1 YUM概述 yum是一个基于RPM包&#xff08;是Red-Hat Package Manager红帽软件包管理器…

【Android入门到项目实战-- 6.1】—— 如何申请用户权限

目录 一、申请权限 1、布局文件 2、MainActivity类 3、AndroidManifest文件 你在使用安卓APP时可能经历过以下场景&#xff1a;使用APP的拍照功能时需要你授权使用相机。那么APP是如何完成申请权限功能的&#xff1f; 访问&#xff1a;https://developer.android.google.cn/…