字符串函数和内存函数的模拟实现

news2024/12/23 23:36:26

目录

前言 

 1. 字符串操作

1.1 strncpy 

1.1.1 讲解

1.1.1 模拟实现

1.2 strncat

1.2.1 讲解

1.2.2 模拟实现

2. 字符串检验

2.1 strlen

2.1.1 讲解

2.1.2 模拟实现

2.2 strstr

2.2.1 讲解

2.2.2 模拟实现

3. 字符数组操作

3.1 memcpy

3.1.1 讲解

3.1.2 模拟实现 

3.2. memmove

3.2.1 讲解

3.2.2 模拟实现


前言 

c语言定义在<string.h>的函数有很多种类(空终止字节字符串 - cppreference.com)我们今天模拟实现以下几种:

1.字符串操作:strncpy,strncat

2.字符串检验:strlen,strstr

3.字符数组操作:memcpy,memmove

 1. 字符串操作

1.1 strncpy 

1.1.1 讲解

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest。 n表示最多复制的字符。最后会返回desk的地址。举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "never give up";
	char arr2[50] = "";
	printf("%s\n", strncpy(arr2, arr1,7));
	return 0;
}

输出结果:never g

我们想将arr1中的数据全部“传输”到arr2中,一般会想到利用for 循环将arr1中的一个元素一个一个拷进去,但其实也可以用strncpy一步到位,7(n),表示复制的字符数。这时,有些小伙伴可能就要问了:如果你要求的字符数大于arr1会怎样?答:不会怎样,\0传进去了,后面传再多也没用了。

1.1.1 模拟实现

首先,凭借着参数及返回值的讲解,我们的函数声明首先就上来了。

char* my_strncpy(char* desk, const char* src, size_t num) ;

有特殊到一般,传了空指针没法处理,返回NULL,正常传进来,处理,也当然会很自然的想到循环。循环num次,一个一个拷贝。

// 模拟实现strncpy

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

char* my_strncpy(char* desk, const char* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	char* ret = desk;
	while (num--)
	{
		*desk++ = *src++;
	}
	return ret;
}

int main()
{
	char arr1[] = "never give up";
	char arr2[50] = "";
	printf("%s\n", my_strncpy(arr2, arr1,strlen(arr1)+1));
	return 0;
}

该段代码简练的一点是使用了后置++,减少了代码量。注意要返回原来的地址,所以还要再创建一个指针变量储存,最后好返回。

1.2 strncat

1.2.1 讲解

C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。举个例子:

#define _CRT_SECURE_NO_WARINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[49] = "never ";
	char arr2[] = "give up";
	printf("%s\n", strncat(arr1, arr2, 6));
	return 0;
}

输出结果:never give u

就好像是把arr1的从\0开始替换成了arr2,前六个字符一样。

1.2.2 模拟实现

你可以这么想象,arr1\0前面是一个数组,从\0开始后面又是一个数组,strncat相当于把arr2中的内容strcpy到了那个后面的数组一样。

 模拟实现strncat
//
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

char* my_strncat(char* desk, const char* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	char* ret = desk;
	while (*desk++)
	{
		;
	}
	desk--;
	while (num--)
	{
		*desk++ = *src++;
	}
	return ret;
}

int main()
{
	char arr1[49] = "never ";
	char arr2[] = "give up";
	printf("%s\n", my_strncat(arr1, arr2, 6));
	return 0;
}

所以,我们先就是找到了\0的地址,其余跟strncpy简直一模一样!

2. 字符串检验

2.1 strlen

2.1.1 讲解

相信大家对strlen函数都不陌生了吧,就是返回\0之前的字符数。

2.1.2 模拟实现

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* arr)
{
	assert(arr);
	const char* start = arr;
	while (*arr++)
	{
		;
	}
	
	return arr - start;
}

int main()
{
	char arr[] = "never give up";
	printf("%zd\n", my_strlen(arr));
	return 0;
}

常见的计数器实现就不过多介绍了,以上便是指针 - 指针的形式。

2.2 strstr

2.2.1 讲解

在wps办公中,我们常有一个查找和替换,strstr就是一个类似于查找功能的函数。char * strstr ( const char * str1, const char * str2); 找的就是str2在str1中首次出现的位置。注意不单检测str1中有没有str2,没有返回空指针,有返回首次出现的地址

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "abbcdefghij";
	char arr2[] = "bcdef";
	printf("%s\n", strstr(arr1, arr2));
	return 0;
}

 输出:bcdefghij

2.2.2 模拟实现

我相信大家开始的思路应该都是在arr1中开始遍历,先找到与arr2第一个字符一样的字符再说,然后看看第二个是否相等,不相等找到的不是,下一个;相等,看看第三个……

由于我们相当于是检验找到前那个元素是否满足要求,肯定就涉及到了while循环。确认第一个字符对不对跟确认第二个,第三个对不对是不是又循环了?你地址在那里变换,如果不是,是不是该储存一个变量来储存,好回滚?

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

// 函数返回substr在str中第一次出现时的地址
char* my_strstr(const char* str, const char* substr)
{
	assert(str);
	assert(substr);
	
	char* temp1 = NULL;
	char* temp2 = NULL;
	int flag = 0;
	while (*substr && *str)
	{
		if (*str++ != *substr)
		{
			if (flag)
			{
				str = ++temp1;
				substr = temp2;
				flag = 0;
				continue;
			}
			temp1 = str;
			temp2 = substr;
		}
		else
		{
			substr++;
			flag = 1;
		}
	}
	if (!flag || *substr)
		return NULL;

	return temp1;
}

int main()
{
	char arr1[] = "abbiijkt";
	char arr2[] = "ijkt";
	char* ret = my_strstr(arr1, arr2);
	printf("%s\n", arr1);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("no find\n");
	return 0;
}

或者说,也可以这么理解,有2个指针跟str有关,有2个指针跟str2有关,其中一种指针的作用是找到并保存str中某个字符跟substr中某个字符相等的地址,另一种指针就看第二个,第三个……是否跟substr2一样,直到\0都一样就返回str前一种指针了,不一样,str前一种指针加1就行了。两个后一个指针回到该回的地方。

3. 字符数组操作

3.1 memcpy

3.1.1 讲解

它跟strncpy很像,都是针对内存进行操作的,但strncpy干不了的,memcpy可以干,因为memcpy可以接收任意类型的指针。C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
	char arr1[49] = "";
	char arr2[] = "never give up";
	printf("%s\n", (char*)memcpy(arr1, arr2 + 1,sizeof(char) * 12));
	int arr3[10] = { 0 };
	int arr4[] = { 1,2,3,4,5,6,7,8,9 };
	memcpy(arr3, arr4, sizeof(int) * 6);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

输出结果:

ever give up
1 2 3 4 5 6 0 0 0 0

这里要注意的是由于memcpy接收的是任意类型的数据,所以也无法判定是什么数据类型,有多少个元素,传过去的是字节数,不是字符数

3.1.2 模拟实现 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* desk, const void* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	void* ret = desk;
	while (num--)
	{
		*((char*)desk)++ = *((char*)src)++;
	}
	return ret;
}

int main()
{
	char arr1[49] = "";
	char arr2[] = "never give up";
	printf("%s\n", (char*)my_memcpy(arr1, arr2 + 1,sizeof(char) * 12));
	int arr3[10] = { 0 };
	int arr4[] = { 1,2,3,4,5,6,7,8,9 };
	my_memcpy(arr3, arr4, sizeof(int) * 6);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

其实,不必因为她接收的是无类型的指针就觉得怎样怎样,就多了一个强制类型转换,如此而已。

3.2. memmove

3.2.1 讲解

其实这个函数跟memmove又是很像,memcpy能干的,memmove能干,memcpy可能干不了的(取决于编译器),memmove能干,memmove多出来的一个功能便是当arr1与arr2有重叠部分时,仍然可以很好的处理。由于在vs2022下memcpy已经设计得跟memmove差不多了,就只演示memmove的了


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>


int main()
{
	char arr[] = "abbcdefg";
	printf("%s\n", arr);
	printf("%s\n", (char*)memmove(arr + 2, arr, 5));
	return 0;
}

3.2.2 模拟实现

这里主要是不同情况拷贝顺序的问题,这么做是为了防止 src中要拷贝给desk中的数因为重叠而被篡改,其他就都跟memcpy一样了。除此以外,从后往前拷,多面的地址通过指针运算就可以得到了,不要陷进去了。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memmove(void* desk, void* src, size_t num)
{
	if (!desk || !src)
	{
		return NULL;
	}
	void* ret = desk;
	if (desk <= src)
	{
		while (num--)
		{
			*((char*)desk)++ = *((char*)src)++;
		}
	}
	else
	{
		while (num--)
		{
			*(((char*)desk) + num) = *(((char*)src) + num);
		}
	}
	return ret;
}

int main()
{
	char arr[] = "abbcdefg";
	printf("%s\n", arr);
	printf("%s\n", (char*)my_memmove(arr + 2, arr, 5));
	return 0;
}

好了,今天字符串函数和内存函数的模拟实现就分享到这里了。

感谢各位大佬的支持和指正

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

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

相关文章

《操作系统实践-基于Linux应用与内核编程》第10章--实验 Qt聊天程序

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

15届蓝桥杯第三期模拟赛所有题目解析

文章目录 &#x1f9e1;&#x1f9e1;t1_奇数次数&#x1f9e1;&#x1f9e1;思路代码 &#x1f9e1;&#x1f9e1;t2_台阶方案&#x1f9e1;&#x1f9e1;思路代码 &#x1f9e1;&#x1f9e1;t3_约数个数&#x1f9e1;&#x1f9e1;思路代码 &#x1f9e1;&#x1f9e1;t4_最…

特殊文本文件、日志技术

特殊文件 为什么要用这些特殊文件&#xff1f; 存储多个用户的&#xff1a;用户名、密码 特殊文件:Properties属性文件 特点&#xff1a; 都只能是键值对键不能重复文件后缀一般是.properties结尾的 作用&#xff1a;存储一些有关系的键值对数据 Properties 是一个Map集合(键…

Ubuntu Argoverse API安装

1. 创建并进入conda环境 conda create -n Argoverse python3.8 conda activate Argoverse2. 拉取argoverse-api源码 git clone https://github.com/argoai/argoverse-api.git3. 下载高精地图 Download hd_maps.tar.gz from Argoverse 4. 安装api cd argoverse-api pip in…

STM32 Simulink 自动代码生成电机控制——霍尔有感六步方波仿真到开发板运行

目录 前言 方波控制理论 仿真 代码集成到开发板运行 总结 前言 回想以前在学校手写代码实现方波控制&#xff0c;花了不少时间。现在各大MCU厂家都有自己的有感或者无感方波控制的方案。这次尝试用Simulink来完成应用层算法的仿真再到代码生成开发板运行的完整流程。 方波…

达梦数据库管理

一、表空间管理介绍 在 DM 数据库中&#xff0c;表空间由一个或者多个数据文件组成。 DM 数据库中的所有对象在逻辑上都存放在表空间中&#xff0c;而物理上都存储在所属表空间的数据文件中。 DM数据库中的表空间可以分为普通表空间和混合表空间。普通表空间不能存储HUGE表&…

《IEEE Transactions on Robotics》发表!北京大学研究团队推出具有多种运动模态的软体两栖机器人

两栖机器人以其在复杂水陆混合环境中的卓越适应性而脱颖而出&#xff0c;成为非结构化场景下信息监测、资源勘探和灾难救援等多元化任务的理想选择。凭借能够在水生和陆生环境中自如切换的优势&#xff0c;两栖机器人在如上任务执行过程中展现出对多变环境的惊人适应能力。 在…

【Godot4.2】颜色完全使用手册

概述 本篇简单汇总Godot中的颜色的构造和使用&#xff0c;内容包括了&#xff1a; RGB、RGBA&#xff0c;HSV以及HTML16进制颜色值、颜色常量等形式构造颜色颜色的运算以及取反、插值用类型化数组、紧缩数组或PNG图片形式存储多个颜色 构造颜色 因为颜色是一种视觉元素&…

配置Web运行环境与第一个网页

安装与配置Web环境: 如下使用了VSC作为web的运行环境。 下面是VSC的官网:Download Visual Studio Code - Mac, Linux, Windowshttps://code.visualstudio.com/download 进入官网后按照自己的系统下载&#xff0c;我下载的是Windows. 下载完之后双击打开。 接下来就是安装&…

探讨TCP的可靠性以及三次握手的奥秘

&#x1f31f; 欢迎来到 我的博客&#xff01; &#x1f308; &#x1f4a1; 探索未知, 分享知识 !&#x1f4ab; 本文目录 1. TCP的可靠性机制1.2可靠性的基础上,尽可能得提高效率 2. TCP三次握手过程3. 为何不是四次握手&#xff1f; 在互联网的复杂世界中&#xff0c;TCP&am…

Autosar Crypto Driver学习笔记(二)

文章目录 Crypto DriverFunction definitionsGeneral APICrypto_InitCrypto_GetVersionInfo Job Processing InterfaceCrypto_ProcessJob Job Cancellation InterfaceKey Management InterfaceKey Setting Interface密钥设置接口Crypto_KeyElementSetCrypto_KeySetValid Key Ex…

Linux操作系统及进程(一)

一、操作系统的概念 概念 &#xff1a;任何计算机系统都包含一个基本的程序集合&#xff0c;称为操作系统(OS)。笼统的理解&#xff0c;操作系统包括&#xff1a; 内核&#xff08;进程管理&#xff0c;内存管理&#xff0c;文件管理&#xff0c;驱动管理&#xff09; 其他程…

Redis淘汰策略,持久化和集群

Redis淘汰策略 Redis 的 OBJECT 命令提供了多个子命令&#xff0c;用于检查有关键的内部详细信息。以下是可用的子命令及其描述&#xff1a; ENCODING <key>&#xff1a;返回用于存储与键关联的值的内部表示类型。 FREQ <key>&#xff1a;返回键的访问频率索引。返…

并查集

数据结构、算法总述&#xff1a;数据结构/基础算法 C/C_禊月初三的博客-CSDN博客 并查集是一种用于管理元素所属集合的数据结构&#xff0c;实现为一个森林&#xff0c;其中每棵树表示一个集合&#xff0c;树中的节点表示对应集合中的元素。 基本操作&#xff1a; 初始化&…

Windows→Linux,本地同步到服务器

适用背景&#xff1a; 用自己电脑修改代码&#xff0c;使用实验室/公司的服务器炼丹的朋友 优势&#xff1a; 本地 <--> 服务器&#xff0c;实时同步&#xff0c;省去文件传输的步骤 本地改 -> 自动同步到服务器 -> 服务器跑代码 -> 一键同步回本地&#xff…

Elasticsearch:调整搜索速度

在我之前的文章 “Elasticsearch&#xff1a;如何提高查询性能” 及 “Elasticsearch&#xff1a;提升 Elasticsearch 性能” 里&#xff0c;我详细描述了如何提高搜索的性能。在今天的文章里&#xff0c;我从另外一个视角来描述如何调整搜索的速度。希望对大家有所帮助&#x…

Python之Web开发中级教程----搭建虚拟环境

Python之Web开发中级教程----搭建Web框架二 搭建虚拟环境 虚拟环境的作用 虚拟环境可以搭建独立的python运行环境, 使得单个项目的运行环境与其它项目互不影响. 搭建虚拟环境 &#xff08;1&#xff09;安装 sudo pip install virtualenv sudo pip install virtualenvwra…

【LabVIEW FPGA入门】定时

在本节学习使用循环计时器来设置FPGA循环速率&#xff0c;等待来添加事件之间的延迟&#xff0c;以及Tick Count来对FPGA代码进行基准测试。 1.定时快捷VI函数 在FPGA VI中放置的每个VI或函数都需要一定的时间来执行。您可以允许操作以数据流确定的速率发生&#xff0c;而无需额…

科研学习|论文解读——交替协同注意力网络下基于属性的情感分析 (IPM, 2019)

论文题目 Aspect-based sentiment analysis with alternating coattention networks 摘要 基于属性的情感分析&#xff08;ABSA&#xff09;旨在预测给定文本中特定目标的情感极性。近年来&#xff0c;为了获得情感分类任务中更有效的特征表示&#xff0c;人们对利用注意力网络…

使用kettle导入Elasticsearch数据库

资源文件&#xff1a; 操作步骤&#xff1a; 1、删除data-integration\lib 下 所有 jackson- 开头的jar包&#xff0c;然后把jackson-2.8.10-libs文件夹中的jar拷到lib目录下 2、替换data-integration\plugins\elasticsearch-bulk-insert-plugin文件夹 3、如果开启了ssl认证…