字符串函数的模拟实现

news2024/11/18 15:35:36

引言:对于字符串来说,我们通常想要对其完成各种各样的目的,不管是排序还是查找都是最普遍的功能,而我们的C语言中也包含着一系列函数是为了实现对字符串的一些功能,今天我们就来介绍他们。


strlen函数:

求字符串的长度(也就是求一个字符串的'\0'前面有多少个字符)

#include<stdio.h>
#include<string.h>
int main(){
	char* str = "abcdef";
	int b = strlen(str);
    printf("%d\n", b);
return 0;
}

 

这个就是strlen最常用的,用来求一个字符串的长度,那么我们是否可以不用库函数,自己模拟一个strlen呢🤔
#include<stdio.h>
#include<string.h>
//方法一:计数法
size_t my_strlen1(char* str) {
	int count = 0;
	while (*str) {
		count++;
		str++;
	}
	return count;
}
int main(){
	char* str = "abcdef";
    int c = my_strlen1(str);
    printf("%d\n", c);
return0;
}

方法一:计数法,利用for循环,用count计数,当str遇到'\0'的时候循环结束,跳出循环,返回count的值。

#include<stdio.h>
#include<string.h>
//方法二:递归法
size_t my_strlen2(char* str) {
	if (*str == '\0')
		return 0;
	else return 1+my_strlen2(str+1);
}
int main() {
	char* str = "abcdef";
	int d = my_strlen2(str);
	printf("%d\n", d);
return 0;
}

 方法二:递归法,利用递归思想,当没有遇到'\0'的时候,就再次调用该函数,直到str遇到'\0',返回0,然后依次再重新返回,就计算出了字符串的长度。

#include<stdio.h>
#include<string.h>
//方法三:指针-指针
size_t my_strlen3(char* str) {
	char* p = str;
	while (*p != '\0') {
		p++;
	}
	return p - str;
}
int main(){
	char* str = "abcdef";
	int e = my_strlen3(str);
	printf("%d\n", e);
	return 0;
}

方法三:指针-指针,我们先将字符串的首元素地址记为str,再将首元素地址传给p,利用while循环找到最后一个元素的地址,当(结束地址-首元素地址)即为字符串长度。

 可以看我们四种方法的运行结果,都是可以正确的求出字符串‘acbdef的长度.

strcpy函数:

#include<stdio.h>
#include<string.h>
int main(){
	char arr1[20] = { 0 };
	char arr2[] = "Hello";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

这个函数的作用就是将arr2中的字符串拷贝到arr1中。那么我们该如何对他进行模拟实现呢🤔

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
char* my_strcpy(char* dest, const char* src) {
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main(){
	char arr1[20] = { 0 };
	char arr2[] = "Hello";
	//strcpy(arr1, arr2);
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

 我们先将两个数组的首元素地址传给函数,因为最终要返回起始地址,所以先将dest给ret,然后运用while循环将src的每一项解引用后赋给dest,然后后置++再寻找下一个元素,直到src元素找到了'\0',然后循环跳出,返回起始地址ret。

我们把arr1打印出来可以看到确实是成功拷贝。 

strcmp函数:

比较的结果是:
如果str1>str2那么就返回一个>0的数 
如果str1<str2那么就返回一个<0的数
如果str1=str2那么就返回0
#include<stdio.h>
#include<string.h>
int main() {
	char arr1[] = { "abc" };
	char arr2[] = { "abdef" };
	int a = strcmp(arr1, arr2);
	if (a > 0) printf(">=");
	else printf("<");
	return 0;
}

这是一个简单的例子,因为d的ASC的值比c的ASC的值大,所以arr1<arr2,所以返回的是一个小于0的数,所以 打印出来的就是小于号。

那么我们来模拟实现实现一下这个函数

#include<stdio.h>
#include<string.h>
int my_strcmp(const char* dest, const char* src){
	while (*dest == *src) {
		if (*dest == 0) return 0;
		dest++;
		src++;
	}
	if (*dest > *src)
		return 1;
	else return -1;
}
int main() {
	char arr1[] = { "abc" };
	char arr2[] = { "abdef" };
	int a = my_strcmp(arr1, arr2);
	if (a > 0) printf(">=");
	else printf("<");
	return 0;
}

 首先,还是一样,先传值然后依旧是运用我们的while循环来进行遍历以及交换,当其中一个已经遍历完之后,会跳出while循环,然后开始判断当前元素谁大谁小,然后根据大小返回大于零还是小于零的数。

strcat函数:

#include<stdio.h>
#include<string.h>
int main() {
	char arr1[20] = { "abc"};
	char arr2[] = { "def" };
	//库函数自带的strcat
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

 值得注意的是:

1、目标空间必须足够大,并且可以修改

2、目标空间中必须有\0,以便能够找到目标空间的末尾

3、源字符串中也得有\0,拷贝的时候要拷过去

那么我们如何对这个函数进行模拟实现呢🤔

#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, char* src) {
	char* ret = dest;
	assert(dest && src);
	while (*dest != '\0') {
		dest++;
	}
	while (*dest++ = *src++) {
		;
	}
	return ret;
}
int main() {
	char arr1[20] = { "abc"};
	char arr2[] = { "def" };
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

 依然是传参,然后用第一个while循环找到目标空间中的\0然后,跳出循环,此时dest指向目标函数的末尾,然后进入第二个循环,src开始给dest进行赋值,直到src遇到\0,跳出循环,返回目标函数的起始地址,完成追加。

strstr函数: 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main() {
	char arr1[] = { "abcdefghi" };
	char arr2[] = { "def" };
	char* ret = strstr(arr1, arr2);
	if (ret == NULL) {
		printf("找不到");
	}
	else printf("%s", ret);
	return 0;
}

strstr是字符串查找函数,我们给函数传两个字符串的时候,他会拿源函数去和目标函数进行匹配,如果目标函数中有原函数那么,就返回段函数的起始地址,如果找不到,那么就会返回NULL

那么我们该如何模拟实现这一函数呢🤔

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
const char* my_strstr(const char* str1, const char* str2) {
	const char* sp;
	const char* s1;
	const char* s2;
	assert(str1 && str2);
	if (*str2 == '\0') {
		return str1;
	}
	sp = str1;
	while (*sp) {
		s1 = sp;
		s2 = str2;
		while (*str1 && *str2 && *s1 == *s2) {
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return sp;
		sp++;
	}
	return NULL;
}
int main() {
	char arr1[] = { "abcdefghi" };
	char arr2[] = { "def" };
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL) {
		printf("找不到");
	}
	else printf("%s", ret);
	return 0;
}

 可以看到,我们模拟实现的函数成功的查找到了目标函数中的def,并且返回了找到它的起始位置。那么具体的实现我来详细的讲解一下。 

如图,我们用s1和s2进行比较,
1️⃣如果两个相同就各自+1比较下一个,那么按照这样进行下去,当str2为0的时候,就证明已经找到了,如果str1已经为0,str2还没有为0那么就证明,str1里找不到str2。
2️⃣如果两个不同就sp++,然后sp再赋值给s1,这样就不会使得s1一直向前++而找不到原来的位置了,s2也是一样,当sp赋值给s1的时候,str2也同时赋值给s2,让s2能回到起始位置。
这样一直对比下去,直到s1或s2指向0,函数结束。那我们这个函数的模拟就算实现了,但是其实这是个笨方法,效率不高,如果大家感兴趣的话,可以去了解了解kmp算法。

strerror函数:

是用来打印错误码的函数

 

 

strtok函数:

是用来将一段字符串进行分割的函数

 

#include<stdio.h>
#include<string.h>
int main() {
	char arr[] = "haohao@xuexi@tiantian.xiangshang";
	char* p = "@.";
	char* s = NULL;
	for (s = strtok(arr, p); s != NULL; s = strtok(NULL, p)) {
		printf("%s\n",s);
	}
	return 0;
}

我们这里可以看到,strtok将arr里的字符串给分隔开了,这个函数可以把指定参数替换成'\0',这样就可以将那些干扰的字符给去掉,并将其一一打印出来了。 

 好了,今天我就给大家分享到这里,感谢大家的观看!!!

 

 

 

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

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

相关文章

基于SpringBoot的车辆管理系统

目录 前言 一、技术栈 二、系统功能介绍 员工信息管理 证件信息管理 车辆信息管理 事故登记管理 事故登记 保养登记 违章登记 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实…

C++设计模式-原型(Prototype)

目录 C设计模式-原型&#xff08;Prototype&#xff09; 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-原型&#xff08;Prototype&#xff09; 一、意图 用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。 二、适用性 当…

@ConfigurationProperties配置绑定~

ConfigurationProperties注解是Spring Boot中的一个注解&#xff0c;用于将配置文件中的属性值绑定到Java类中的字段上。 ConfigurationProperties注解的作用包括&#xff1a; 实现配置文件属性和Java类字段的映射&#xff0c;简化了读取配置文件的操作。 可以指定配置文件中…

1.3.OpenCV技能树--第一单元--图像的基础操作(基础篇)

文章目录 1.文章内容来源2.图像的基本操作2.1.图像加载2.2.图像显示2.3.数据读取2.4.截取图像2.5.颜色通道提取2.5.1.保留红色处理2.5.2.保留绿色处理2.5.3.保留蓝色处理 3.易错点总结与反思 1.文章内容来源 1.题目来源: 2.资料来源:https://edu.csdn.net/skill/opencv/opencv…

ALSA pcm接口的概念解释

PCM(数字音频)接口 PCM缩写: Pulse Code Modulation脉冲调制编码,我们理解为通过一定连续时间周期产生数字音频并带有音量样本的处理过程. 模拟信号被记录通过模拟到数字转换器,数字值(也就是某个特定时刻的音量值)获得来自ADC可以进一步处理,接下的图片展示的是个sine wavefor…

【BBC新闻文章分类】使用 TF 2.0和 LSTM 的文本分类

一、说明 NLP上的许多创新是如何将上下文添加到词向量中。常见的方法之一是使用递归神经网络

【user_key_payload、msg_msg、pipe_buffer】再探RWCTF2023-Digging-into-kernel-3

前言 在之前的文章中&#xff0c;我利用 ldt_struct 去泄漏的内核基地址&#xff0c;但是在内核中还存在着一些结构体可以去泄漏内核基地址。 user_key_payload 越界读泄漏内核基地址 本题并没有开启 slab_freelist_random 保护&#xff0c;并且可以可以同时控制两个堆块&am…

[图论]哈尔滨工业大学(哈工大 HIT)学习笔记16-22

视频来源&#xff1a;2.7.1 补图_哔哩哔哩_bilibili 目录 1. 补图 1.1. 补图 2. 双图 2.1. 双图定理 3. 图兰定理/托兰定理 4. 极图理论 5. 欧拉图 5.1. 欧拉迹 5.2. 欧拉闭迹 5.3. 欧拉图 5.4. 欧拉定理 5.5. 伪图 1. 补图 1.1. 补图 &#xff08;1&#xff09;…

使用图形视图框架(Graphics View Framework)在QML中创建交互式图形界面

使用图形视图框架(Graphics View Framework)在QML中创建交互式图形界面 使用图形视图框架(Graphics View Framework)在QML中创建交互式图形界面 使用图形视图框架(Graphics View Framework)在QML中创建交互式图形界面什么是图形视图框架(Graphics View Framework)&#xff1f;…

MATLAB算法实战应用案例精讲-【优化算法】沙丁鱼优化算法(SOA)(附MATLAB代码实现)

前言 沙丁鱼优化算法(Sardine optimization algorithm,SOA)由Zhang HongGuang等人于2023年提出,该算法模拟沙丁鱼的生存策略,具有搜索能力强,求解精度高等特点。 沙丁鱼主要以浮游生物为食,这些生物包括细菌、腔肠动物、软体动物、原生动物、十足目、幼小藤壶、鱼卵、甲藻…

cartographer-(0)-ubuntu(20.04)-环境安装

1.安装 ROS wiki.ros.org 1.1修改镜像源&#xff1a; 到网站上找与操作系统相匹配的镜像源 ubuntu | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror # 默认注释了源码镜像以提高 apt update 速度&#xff0c;如有需要可自行取消注释 deb htt…

MySQL基础-事务

目录 1.事务简介 2.事务的操作 2.1 实验需要用到的数据 2.2 完成转账操作 修改事务执行方式 手动开启事务的方式 3.事务的四大特性 4.并发事务问题 1.事务简介 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一…

【开发篇】十五、Spring Task实现定时任务

文章目录 1、使用示例2、相关配置3、Scheduled注解4、Spring Task单线程下的阻塞坑5、Spring Task阻塞问题的处理思路6、Spring Task在分布式环境中 上一篇用Quartz来实现了定时任务&#xff0c;但相对来说&#xff0c;这个框架还是比较繁琐。Spring Boot默认在无任何第三方依赖…

一天销量200万,国产5G手机20天才200万,挑战iPhone也就想想罢了

国产5G手机频频放话要挑战iPhone&#xff0c;不过现实却相当打脸&#xff0c;果粉对苹果的忠诚丝毫没有受到影响&#xff0c;销量是最直接的表现&#xff0c;那就是国产5G手机20天才卖出200万部&#xff0c;而这仅仅是iPhone15一天的销量。 iPhone15在发布前曾拉高了消费者的期…

【应用层协议】初始Http,fiddler的使用

文章目录 1. HTTP概念2. 下载fiddler及使用获得HTTP协议格式2.1 fiddler的下载2.2 fiddler使用 3. HTTP请求&#xff08;Request&#xff09;3.1 请求行3.1.1 URL3.1.2 方法3.1.2.1 GET3.1.2.2 POST3.1.2.3 其他方法 3.2 报头&#xff08;header&#xff09;3.3 空白行3.4 正文…

Django 模型层的操作(Django-05 )

一 模型层的解读 Django中内嵌了ORM框架&#xff0c;不需要直接编写SQL语句进行数据库操作&#xff0c;而是通过定义模型类&#xff0c;操作模型类来完成对数据库中表的增删改查和创建等操作。 O 是object&#xff0c;也就类对象的意思。R 是relation&#xff0c;翻译成中文是…

Python综合案例:学生管理系统

目录 需求说明&#xff1a; 功能&#xff1a; 创建入口函数&#xff1a; 实现菜单函数&#xff1a; 实现增删查操作&#xff1a; 1. 新增学生 2. 展示学生 3. 查找学生 4. 删除学生 加入存档读档&#xff1a; 1. 约定存档格式 2. 实现存档函数 3. 实现读档函数 打…

C#中的数组探究与学习

目录 C#中的数组一般分为:一.数组定义:为什么要使用数组?什么是数组?C#一维数组for和foreach的区别C#多维数组C#锯齿数组初始化的意义:适用场景:C#中的数组一般分为: ​①.一维数组。 ②.多维数组,也叫矩形数组。 ③.锯齿数组,也叫交错数组。 一.数组定义: 数组…

013-第二代上位机开发环境搭建

第二代上位机开发环境搭建 文章目录 第二代上位机开发环境搭建项目介绍虚拟机安装Debian 10文件传输远程调试VNCrsync下载安装验证 配置远程调试环境配置远程设备配置 kitsCompilers配置Qtversions配置kits 测试 总结一下 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4…

GitHub爬虫项目详解

前言 闲来无事浏览GitHub的时候&#xff0c;看到一个仓库&#xff0c;里边列举了Java的优秀开源项目列表&#xff0c;包括说明、仓库地址等&#xff0c;还是很具有学习意义的。但是大家也知道&#xff0c;国内访问GitHub的时候&#xff0c;经常存在访问超时的问题&#xff0c;…