模拟实现字符串库函数(一)

在C语言的标准库中提供了很多针对字符串的库函数,这篇文章我们会学习并模拟实现几个简单的库函数

求字符串长度函数strlen

strlen函数我们在之前已经用过很多次了,同时也模拟实现过,但是都不是模仿标准库中的strlen来实现,首先我们在cplusplus中找到strlen函数的介绍

从介绍中我们知道strlen的返回值是一个size_t的无符号整型,参数是一个const修饰的字符指针。而strlen的功能是求字符串的长度,他以'\0'为结束标志,返回的是 '\0' 之前的字符个数。当我们传过去的字符串内容没有 '\0' ,strlen函数会一直向后访问知道找到 '\0' 。所以我们在使用strlen函数时一定要确定字符串结尾有结束标志。

strlen的模拟实现代码如下:

size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
	{
		return 1 + my_strlen(str + 1);
	}
	else
	{
		return 0;
	}
}

长度不受限制的字符串函数

strcpy

strcpy的两个参数分别是char* destination 要拷贝的目标地址,char* source 拷贝的源头,返回的是目标地址,不过这个函数我们在使用时一般不会用一个指针来接收返回值。 这个函数的特点就是遇到 '\0' 结束,拷贝的是 '\0' 及以前的字符。如果source传过来的字符串中没有 '\0' ,这个函数就会一直向后访问拷贝,直到遇到 '\0' ,与strlen类似,所以我们在使用这个函数时要做好参数的设置。

使用的时候要注意的是,要拷贝的字符串一定要有结束标志,目标空间一定要足够,目标空间一定要可修改,不能传const修饰的或者常量的指针。同时要注意不要更改source的内容。

char* my_strcpy(char* dest, const char* source)
{
    assert(dest&&source);
    char* ret=dest;
	while (*dest++=*source++)
	{
		;
	}
}

strcat

strcat是一个字符串追加函数,两个参数 destination和source分别是被追加的字符串起始地址和追加的字符串的地址,要注意目标空间一定要足够放得下追加后的字符串,同时目标空间一定要可修改。 

模拟实现这个函数的第一步便是要找到destination中字符串的结尾,然后从这个位置开始追加,追加后会补一个'\0',可以理解为把source字符串的'\0'也追加上去了。

char* my_strcat(char* dest, const char* source)
{
	assert(dest && source);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *source++)
	{
		;
	}
}

在我们实现的这个函数是没办法实现自己给自己追加(dest和source有重叠部分),因为追加的时候覆盖原字符串,导致'\0'也会被覆盖,死循环。

strcmp

strcmp函数是一个字符串比较函数,两个参数是要比较的两个字符串。如果第一个字符串小于第二个字符串,就返回小于0的整型,如果两个字符串内容相等则返回0,否则返回大于0的整型。这个函数是逐字符比较,比较的是两个字符的ASCII码值。在使用时也要注意检查参数有没有结束标志。

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2&&*s1!='\0')
	{
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

长度受限制的字符串函数

strncpy

strncpy相对于strcpy多了一个num参数,表示要拷贝的字符个数,拷贝完之后会补一个'\0',要注意num不要大于source的长度,当num大于source长度时会用'\0'来补充。

char* my_strncpy(char* dest, const char* source,size_t num)
{
	assert(dest && source);
	char* ret = dest;
	while (*source!='\0'&&num--)
	{
		*dest = *source;
		dest++;
		source++;
	}
	if (num > 0)
	{
		while (num--)
		{
			*dest++ = '\0';
		}
	}
	*dest = '\0';
	return ret;
}

strncat

与strcat相比多了一个num参数,表示要追加的字符,注意事项一样,同时,strcat再追加完num个字符后会在后面补一个'\0'。num长度不能大于source长度。

char* my_strncat(char* dest, const char* source, size_t num)
{
	assert(dest, source);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (num--)
	{
		*dest++ = *source++;
	}
	*dest = '\0';
	return ret;
}

strncmp

比较两个字符串前num个字符的大小。

int my_strncmp(const char* s1, const char* s2, size_t num)
{
	assert(s1 &&s2);
	while (*s1 == *s2 &&num--)
	{
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

查找子串函数strstr

可以看到这个函数有两个参数,作用是再str1中查找是否有字串str2,如果有,返回str1中字串的起始地址,如果没有字串,返回空指针。

这个函数模拟实现的思路就是遍历str1,如果有字符与str2首字符相同,则比较是不是字串。要注意的是,遍历str1时要用两个指针,一个用来遍历str1并在比较时记录当前位置,一个用来判断是不是字串。

char* my_strstr(const char* s1, const char* s2)
{
	assert(s1 && s2);
	char* begin = s1;
	char* cmp1 = begin;
	char* cmp2 = s2;
	while (*begin != '\0')
	{
		if (*begin == *s2)
		{
			cmp1 = begin;
			cmp2 = s2;
			while (*cmp2 != '\0'&&*cmp1==*cmp2)
			{
				cmp1++;
				cmp2++;
			}
			if (*cmp2 == '\0')//匹配成功
			{
				return begin;
			}
		}
		begin++;
	}
	//前面没有返回就意味着找不到子串
	return NULL;
}

切割字符串函数strtok

这个函数两个参数,str是要切割的字符,delimiters是个字符串,定义了用作分隔符的字符集合。这个函数的作用就是str中遇到delimiters中的字符就标记,把这个标记改成'\0',并返回这一子字符串的地址,同时strtok会保存这个标记的下一个位置,如果下一次使用strtok函数时第一个参数传的是NULL,strtok就会从这个位置开始继续查找下一个标记。当我们传的字符串中有多个分隔符,我们只需要第一次调用strtok时传字符串,之后调用就传NULL来找第二个标记。如果字符串结束都没找到标记,就返回空指针。

我们在模拟实现的时候要注意用static修饰保留上一次查找到的地址。

如果两个分隔符连在一起,就跳过这些连在一起的分隔符,因为这两个分隔符之间没有元素。

char* my_strtok(char* str, const char* sep)
{
	assert(sep);
	char* s2 = sep;
	static char* begin ;

	if (str != NULL)
	{
		begin = str;
	}
	if (*begin == '\0')//遍历完了str,返回空指针
	{
			return NULL;
	}

	

	char* s1 = begin;
	while (*s1 != '\0')//跳过字符串开始的分隔符
	{
		int flag = 0;
		s2 = sep;
		while (*s2 != '\0')
		{

			if (*s1 == *s2)
			{
				flag = 1;
				*s1 = '\0';
				begin++;
				break;
			}
			s2++;
		}
		if (flag == 0)
		{
			break;
		}
		else
		{
			s1++;
		}
	}
	//找分隔符
	while (*s1 != '\0')
	{
		s2 = sep;
		int flag = 0;
		while (*s2 != '\0')
		{
			if (*s2 == *s1)
			{
				flag = 1;
				break;
			}
			s2++;
		}
		if (flag == 1)
		{
			*s1 = '\0';
			char* ret = begin;
			begin = s1+1;
			return ret;
		}
		else
		{
			s1++;
		}
	}
	//str遍历结束返回后面的字符
	char* ret = begin;
	begin = s1;
	return ret;
	
}

这段代码可能有点难理解,主要是begin指针的操作有点复杂。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三.寄存器(内存访问)

1.内存中字的存储 2.并不是所有cpu都支持将数据段送入段寄存器,所以有时候用个别的寄存器先把数据段存储起来,再把该寄存器mov到段寄存器。 3.字的传送 4.栈 5.栈机制 举例说明 6.栈顶超界问题 push超界 pop超界 7.栈段

pta-洛希极限

科幻电影《流浪地球》中一个重要的情节是地球距离木星太近时,大气开始被木星吸走,而随着不断接近地木“刚体洛希极限”,地球面临被彻底撕碎的危险。但实际上,这个计算是错误的。 洛希极限(Roche limit)是一…

python写爬虫爬取京东商品信息

工具库 爬虫有两种方案: 第一种方式是使用request模拟请求,并使用bs4解析respond得到数据。第二种是使用selenium和无头浏览器,selenium自动化操作无头浏览器,由无头浏览器实现请求,对得到的数据进行解析。 第一种方…

实战高效RPC方案在嵌入式环境中的应用与揭秘

实战高效RPC方案在嵌入式环境中的应用与揭秘 开篇 在嵌入式系统开发中,大型项目往往采用微服务架构来构建,其核心思想是将一个庞大的单体应用分割成一系列小型、独立、松耦合的服务模块,这些模块可以是以线程或进程形式存在的多个服务单元。…

OpenHarmony开发-线程安全阻塞队列

概述 简介 ​线程安全阻塞队列SafeBlockQueue类&#xff0c;提供阻塞和非阻塞版的入队入队和出队接口&#xff0c;并提供可最追踪任务完成状态的的SafeBlockQueueTracking类。 #include <safe_block_queue.h> 涉及功能 接口说明 OHOS::SafeBlockQueue OHOS::SafeBl…

[Java C++] JNI开发

JNI&#xff08;Java Native Interface&#xff09;是 Java 提供的一种编程桥梁&#xff0c;它允许 Java 代码和本地&#xff08;Native&#xff09;代码进行交互。通过 JNI&#xff0c;Java 程序可以调用本地语言&#xff08;如C、C&#xff09;编写的代码&#xff0c;并且本地…

如何用python编写记录你女友的生日呢?

如何用python编写记录你女友的生日呢&#xff1f; 我这边写一个简单的 Python 程序示例,可以用来记录生日.这个程序将用户输入的姓名和生日信息保存到一个字典中,并允许用户查找特定姓名对应的生日信息. def record_birthday():birthdays {}while True:print("1. 添加生…

IP地址、子网掩码、网关

这些概念的来源 很久以前&#xff0c;有两个计算机想要相互通信&#xff0c;于是它们在自己的设备上安装了一个网卡&#xff0c;并用网线连接&#xff1a; 这个时候&#xff0c;又来了一个计算机想要加入它们&#xff0c;于是这三个计算机互相通过网线连接&#xff1a; 随着想…

taro之Swiper的使用

图样&#xff1a; 往往我们需要轮播图去显示我们想要的图片之类的 这是工作的代码 <View classNametop-title><SwiperclassNamebanner-swiperinterval{3000}circularautoplay>{homeBannerList.map((item) > {return (<SwiperItem key{item.id}><View…

Linux之git

一、什么叫做版本控制 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。简单来说就是用于管理多人协同开发项目的技…

MySQL临时表:临时存储数据的灵活利器

MySQL临时表&#xff1a;临时存储数据的灵活利器 MySQL临时表是处理数据时非常有用的工具&#xff0c;它提供了临时存储数据的能力&#xff0c;使得复杂查询、排序、聚合以及数据筛选变得更加高效和简单。在本文中&#xff0c;我们将深入探讨MySQL临时表的概念以及何时需要使用…

【算法刷题day1】Leetcode:704. 二分查找、27. 移除元素

文章目录 Leetcode 704. 二分查找解题思路代码总结 Leetcode 27. 移除元素解题思路代码总结 草稿图网站 java的Deque Leetcode 704. 二分查找 题目&#xff1a;704. 二分查找 解题思路 1.左闭右闭区间的搜索&#xff0c;循环条件为left < right。 2.左闭右开区间的搜索&…

C++一维数组练习oj(3)

为什么C的一维数组练习要出要做那么多的题目&#xff1f;因为我们是竞赛学生&#xff01;想要将每个知识点灵活运用的话就必须刷大量的题目来锻炼思维。 我使用的是jsswoj.com这个刷题网站&#xff0c;当然要钱... C一维数组练习oj(2)-CSDN博客这是上一次的题目讲解 这道题有…

Unity学习笔记 6.2D换帧动画

下载源码 UnityPackage 目录 1.导入图片 1.1. 图片的叠放顺序 2.图片切片 3.用动画控制器让马&#x1f40e;动起来 1.导入图片 直接拖拽进场景 检查 Texture Type&#xff08;纹理类型&#xff09;是否为 Sprite 创建2D精灵对象&#xff0c;拖拽图片到Sprite&#xff08…

【C++】关联式容器——map和set

1 关联式容器 STL中我们常用的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 那什么是关联式容器呢&#xff1f;它与序…

蓝桥杯G431RBT6——定时器中使用led冲突以及led与lcd冲突等一系列问题

本文是解决 同时在 定时器中点灯 与 LCD屏幕显示 冲突异常的问题 我们大家都知道&#xff0c;G431RBT6开发板上led与lcd是冲突的&#xff0c;所以在lcd.c文件中的这三个函数中 void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue) void LCD_WriteRAM_Prepare(void) void LCD_Wr…

移动0【双指针】

移动零 cur每次走一步&#xff0c;dest走不走取决于cur有没有找到非0值&#xff0c;一旦找打非0值&#xff0c;交换。不是非0值&#xff0c;dest不动。》【非零&#xff0c;dest】【dest&#xff0c;0】 class Solution { public:void moveZeroes(vector<int>& num…

算法第三十二天-最长公共子序列

最长公共子序列 题目要求 解题思路 求这两个数组或者字符串的最长公共子序列问题&#xff0c;肯定要用到动态规划。 首先区分两个概念&#xff1a;子序列可以是不连续的&#xff1b;子数组&#xff08;子字符串&#xff09;是需要连续的&#xff1b;另外&#xff0c;动态规划…

制冷设备之转子式压缩机

滚动转子式压缩机又称活塞式压缩机&#xff0c;属于回转式压缩机。 转子压缩机结构 滚动转子式压缩机与往复活塞式压缩机相比&#xff0c;具有下列特点 1.零部件少&#xff0c;尺寸紧凑&#xff0c;结构简单&#xff0c;重量轻易损零件少&#xff0c;运行可靠&#xff1b; 2.…

C语言动态内存的管理

前言 本篇博客就来探讨一下动态内存&#xff0c;说到内存&#xff0c;我们以前开辟空间大小都是固定的&#xff0c;不能调整这个空间大小&#xff0c;于是就有动态内存&#xff0c;可以让我们自己选择开辟多少空间&#xff0c;更加方便&#xff0c;让我们一起来看看动态内存的有…