C语言字符串库函数模拟实现

news2024/11/18 18:45:42

字符串检验

strlen

函数原型

/// @brief 返回给定空终止字符串的长度,即首元素为 str 所指,且不包含首个空字符的字符数组中的字符数
/// @param str 指向要检测的字符串的指针
/// @return 字符串 str 的长度
size_t strlen( const char *str );

空终止字符串即 C 语言中以 ‘\0’ 作为终止符的字符串,strlen 计算字符串的长度并返回,返回的长度不包含 ‘\0’。

char arr[] = "qgw";
// num 的值是 3
int num = strlen(arr);

模拟实现

下面的实现方式比较简单,采用遍历字符串的方式,遇到 ‘\0’ 就退出循环,返回结果。

size_t my_strlen(const char* str) {
	size_t cnt = 0;
	while (*str != '\0') {
		++str;
		++cnt;
	}
	return cnt;
}

strcmp

函数原型

/// @brief 以字典序比较二个字符串
/// @param str rhs 要比较的两字符串
/// @return 相等返回 0
int strcmp(const char* lhs, const char* rhs);

返回值:

  • 两字符串字典序相同,返回 0
  • lhs 字典序大于 rhs,返回一个正数
  • lhs 字典序小于 rhs,返回一个负数

模拟实现

实现的思路是:遍历两个字符串,直到出现不同有字符串结束

res 存储两字符串比较的结果,如果等于 0 循环继续,不等于 0 直接退出,lhs 指向的字符大于 rhs 指向的字符就为正数,否则反之。

若 lhs 先结束,rhs 还没结束,此时 res 为负数,\0 的 ASCII 码为 0,是最小的。若 rhs 先结束,lhs 还没结束,此时 res 为正数。若同时结束,res 刚好为 0 返回。

int my_strcmp(const char* lhs, const char* rhs) {
	int res = 0;
	while ((res = *lhs - *rhs) == 0 && *lhs != '\0' && *rhs != '\0') {
		++lhs;
		++rhs;
	}
	return res;
}

strstr

函数原型

/// @brief 在 str 中查找 substr 子串
/// @param str 指向要检验的空终止字符串的指针
/// @param substr 指向要查找的空终止字节字符串的指针
/// @return 指向于 str 中找到的子串首字符的指针,或若找不到该子串则为空指针
char *strstr( const char* str, const char* substr );

若 substr 指向空,则会返回 str。

模拟实现

下面实现的方法为暴力匹配子串,实际中可以使用 KMP 或 BM 等字符串搜索函数优化这一过程。

char* my_strstr(const char* str, const char* substr) {
	if (substr == NULL) {
		return str;
	}

	// 遍历 str 所有字符,看以其起始字符是否匹配
	while (*str != '\0') {
		const char* backStr = str;
		const char* backSub = substr;
		while (*backStr != '\0' && *backSub != '\0' && *backStr == *backSub) {
			++backStr;
			++backSub;
		}
		// 如果 substr 走完了,说明匹配成功了,返回此时的 str
		if (*backSub == '\0') {
			return str;
		}
		++str;
	}
}

字符串操作

strcpy

函数原型

/// @brief 复制 src 所指向的空终止字符串,包含空终止符,到首元素为 dest 所指的字符数组
/// @param dest 指向要写入的字符数组的指针
/// @param src 指向要复制的空终止字符串的指针
/// @return 返回 dest 的副本
char *strcpy( char *dest, const char *src );

需要注意的是:

  • 若 dest 数组长度不足则行为未定义
    • 即 dest 数组不足以包含 src 中所有元素,此时大概率会因越届访问崩溃
  • 若字符串覆盖则行为未定义
    • 现在主流编译器都可以处理有覆盖的情况,比如:MSVC、GCC
  • 若 dest 不是指向字符数组的指针或 src 不是指向空终止字符串的指针则行为未定义

模拟实现

下面的方法先记录要返回的地址,最后遍历 src 遇到 \0,此时退出循环。

注意:该实现方法并不能解决字符串有覆盖的情况。

char* my_strcpy(char* dest, const char* src) {
	char* res = dest;
	while (*dest = *src) {
		++dest;
		++src;
	}
	return res;
}

strcat

函数原型

/// @brief 后附 src 所指向的空终止字符串的副本到 dest 所指向的空终止字符串的结尾
/// @param dest 指向要后附到的空终止字符串的指针
/// @param src 指向作为复制来源的空终止字符串的指针
/// @return 返回 dest 的副本
char *strcat( char *dest, const char *src );

需要注意的是:

  • 会用字符 src[0] 替换 dest 末尾的 \0
  • 若目标数组对于 src 和 dest 的内容以及空终止符不够大,则行为未定义
  • 若字符串重叠,则行为未定义
  • 若 dest 或 src 不是指向空终止字符串的指针,则行为未定义

模拟实现

因为会先用 src 第一个字符替换 dest 结尾的 \0,所以要先找到 dest 结尾 \0。然后再遍历 src,将其添加到 dest 结尾。

char* my_strcat(char* dest, const char* src) {
	char res = dest;
	// 先找 \0 的位置
	while (*dest != '\0') {
		++dest;
	}
	// 追加到 dest 后面
	while (*dest = *src) {
		++dest;
		++src;
	}
	return res;
}

内存操作

memcpy

函数原型

/// @brief 从 src 所指向的对象复制 count 个字符到 dest 所指向的对象
/// @param dest 指向要复制的对象的指针
/// @param src 指向复制来源对象的指针
/// @param count 复制的字节数
/// @return 返回 dest 的副本
void* memcpy(void* dest, const void* src, size_t count);

要注意的是:

  • 若访问发生在 dest 数组结尾后则行为未定义
  • 若对象重叠,则行为未定义
    • 也就是说在标准中 memcpy 也不能处理对象重叠的情况
  • 若 dest 或 src 为非法或空指针则行为未定义

模拟实现

下面给出的实现方式与 strcpy 相似,不过是改用 count 变量来控制循环次数。

memcpy 是最快的内存到内存复制子程序。它通常比必须扫描其所复制数据的 strcpy,或必须预防以处理重叠输入的 memmove更高效。

void* my_memcpy(void* dest, const void* src, size_t count) {
	void* res = dest;
	while (count--) {
		*(char*)dest = *(char*)src;
		++(char*)dest;
		++(char*)src;
	}
	return res;
}

memmove

函数原型

/// @brief 从 src 所指向的对象复制 count 个字节到 dest 所指向的对象
/// @param dest 指向要复制的对象的指针
/// @param src 指向复制来源对象的指针
/// @param count 复制的字节数
/// @return 返回 dest 的副本
void* memmove(void* dest, const void* src, size_t count);

memmove 是不是看起来和 memcpy 一模一样,但 memmove 可以处理内存重叠的情况,这也就说明它要做一些检查,来保证能够处理内存重叠的情况。

  1. 无重叠
    • 直接调用更高校的 memcpy
  2. 有重叠,dest 在 src 之前,正常正向复制

Forward replication

  1. 有重叠,dest 在 src 之后,需要反向复制

foreard copy

reverse copy

模拟实现

通过对上图的观察,我们可以发现:若 dest 在 src 的后面并且存在内存重叠,就需要反向复制。

我们可以简化这一函数,对无重叠的情况不去调用 memcpy 而是包含在下面两种情况中。

  1. dest 在 src 之前,采用正向复制
  2. dest 在 src 之后,采用反向复制
void* my_memmove(void* dest, const void* src, size_t count) {
	void* res = dest;
	if (dest < src) {
		while (count--) {
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	} else {
		while (count--) {
			*((char*)dest + count) = *((char*)src + count);
		}
	}
}

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

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

相关文章

C语言实现通讯录静态版本

通讯录中首先要有人的信息&#xff0c;然后是存放多少个人的信息 再丰富一下通讯录的功能&#xff0c;例如增删查改、显示、排序。 我们分三个文件来实现。 1、实现简易的菜单&#xff0c;通讯录的整体逻辑 #include"contact.h"void menu() {printf("*****…

900页文档比对只需5分钟?鸿翼InWise文档比对,以人工智能撬动办公效率杠杆

在日常办公中&#xff0c;多份文件间的检查、纠错、复核工作不可避免&#xff0c;这类工作往往具有很强的重复性&#xff0c;占用了大量的工作时间。鸿翼InWise平台文档比对能够赋能企业极速完成海量文档、图片的高精度比对&#xff0c;以人工智能撬动企业生产力提升。 随着数字…

MySQL调优-MySQL索引深入总结

目录 MySQL索引深入总结 InnoDB中的索引复习 聚集索引/聚簇索引 问题&#xff1a;如果我们没有定义主键呢&#xff1f; 问题&#xff1a;分析一下B树三层和四层的性能差异&#xff1f; 辅助索引/二级索引 回表 问题&#xff1a;为什么我们还需要一次回表操作呢?直接把完…

亚马逊云科技:小鹏汽车拓展全球市场,跑出“加速度”

汽车产业变革走向下半场&#xff0c;智能汽车的市场份额之争也从国内走向国际&#xff0c;出海之战讲求速战速决&#xff0c;小鹏汽车携手亚马逊云科技拓展海外市场&#xff0c;完成海外布局。 扩大“鹏”友圈&#xff0c;迈进欧洲市场 近年来&#xff0c;小鹏汽车不断推进全…

数字人民币年度总结:支付变革未停、试点之风再起

文/尹宁出品/陀螺研究院数字人民币&#xff0c;无疑是近年来热度最高的词汇之一&#xff0c;作为我国法币的数字形式&#xff0c;至其出世伊始&#xff0c;不论是资金溯源的透明追踪、零售更新的消费用途&#xff0c;还是跨境结算的雄图大略&#xff0c;围绕其推广与意义的讨论…

关于 国产麒麟系统上长时间运行Qt程序根目录/下磁盘空间占用100%导致无法写入 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/128671382 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

LeetCode 129. 求根节点到叶节点数字之和

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 129. 求根节点到叶节点数字之和&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名…

4.6 集成运放的使用

一、使用时必做的工作 1、集成运放的外引线&#xff08;管脚&#xff09; 目前集成运放的常见封装方式有金属壳封装和双列直插式封装&#xff0c;外形如图4.6.1所示&#xff0c;以后者居多。双列直插式有 8、10、12、14、16 管脚等种类&#xff0c;虽然它们的外引线排列日趋标…

laravel JWT 用户认证 实现API认证

JWT 是 JSON Web Token 的缩写&#xff0c;它是一个规范&#xff0c;让用户和服务器之间传递安全可靠的信息。 JWT介绍 JWT 由头部&#xff08;header&#xff09;、载荷&#xff08;payload&#xff09;与签名&#xff08;signature&#xff09;组成 { “typ”:“JWT”, “a…

python的8大核心语句,你确定不来看看嘛,那格局就小啦

Python 是一种代表简单思想的语言&#xff0c;其语法相对简单&#xff0c;很容易上手。不过&#xff0c;如果就此小视 Python 语法的精妙和深邃&#xff0c;那就大错特错了。本文精心筛选了最能展现 Python 语法之精妙的十个知识点&#xff0c;并附上详细的实例代码。如能在实战…

学习使用 Clion 第一次使用Clion开发Qt的hello world

为什么使用Clion之前在window上开发软件基本上是使用VS2022,使用Clion在windows上开发并不是嫌弃VS2022不好用,相反我依然觉得VS是世界上最好用的IDE没有之一.没有其他的理由,这是我第一款使用的IDE,而且已经很使用的熟练.那为什么还要学习使用Clion了,其实是考虑到跨平台.VS有…

【C进阶】 字符串函数和字符分类函数

家人们欢迎来到小姜的世界&#xff0c;<<点此>>传送门 这里有详细的关于C/C/Linux等的解析课程&#xff0c;家人们赶紧冲鸭&#xff01;&#xff01;&#xff01; 客官&#xff0c;码字不易&#xff0c;来个三连支持一下吧&#xff01;&#xff01;&#xff01;关注…

Linux操作系统-信号

信号的基本认识&#xff1a;Linux信号机制&#xff1a;它是一种异步的通知机制&#xff0c;用来提醒进程一个事件已经发生。如上图&#xff0c;Linux操作系统中&#xff0c;共有编号为1~31的31个普通信号&#xff0c;编号为34~64的31个实时信号。日常中只会涉及和使用到普通信号…

说说你对Spring三级缓存的理解

这个问题或者换个问法&#xff1a;Spring是如何解决循环依赖的&#xff1f;答案即是Spring的三级缓存 什么是循环依赖&#xff1f; 简单说&#xff0c;就是A对象依赖B对象&#xff0c;B对象⼜依赖A对象&#xff0c;类似的代码如下&#xff1a; 其他还有很多种⽅式&#xff0…

内核解读之内存管理(12)进程虚拟内存管理 vm_area_struct 与反向映射

在32位的系统上&#xff0c;线性地址空间可达到4GB&#xff0c;这4GB一般按照3:1的比例进行分配&#xff0c;也就是说用户进程享有前3GB线性地址空间&#xff0c;而内核独享最后1GB线性地址空间。由于虚拟内存的引入&#xff0c;每个进程都可拥有3GB的虚拟内存&#xff0c;并且…

靶机测试ReconForce笔记

靶机地址https://www.vulnhub.com/entry/hacknos-reconforce,416/靶机测试信息收集nmap扫描端口nmap扫描结果└─$ nmap -sC -sV 192.168.1.100 -oA hack …

结合NWR,让Paxos拥有的动态的Quorum,以及在Klein中的实践

Paxos Quorum面临的困境在原生的Basic-Paxos或者Multi-Paxos中&#xff0c;Quorum的数量要求的是多数派&#xff0c;例如&#xff1a;一个5成员组成的Paxos集群&#xff0c;Prepare和Accept阶段需要获得3个Acceptor的支持。Quorum3的条件&#xff0c;在原生的Paxos中是硬性条件…

NTP服务器(GPS北斗授时服务器)的市场需求及性能分析

NTP服务器&#xff08;GPS北斗授时服务器&#xff09;的市场需求及性能分析 NTP服务器&#xff08;GPS北斗授时服务器&#xff09;的市场需求及性能分析 目前国内NTP时间服务器时间精度已达到毫秒级&#xff0c;能够满足各个行业对时间同步运转的要求&#xff1b;有些企业需要…

QtitanComponents Solution 2022.4.0 Crack

2022-12-29 开始开发基于Adobe XD文件的Qt样式创建工具 包括&#xff1a; QtitanDataGrid QtitanRibbon QtitanNavigationDesignUI QtitanChart QtitanDocking QtitanFastInfoset 不要问我源码&#xff0c;没有&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#x…

Spring Boot 加载外部配置文件

Spring Boot 允许你从外部加载配置&#xff0c;这样的话&#xff0c;就可以在不同的环境中使用相同的代码。支持的外部配置源包括&#xff1a;Java属性文件、YAML文件、环境变量、命令行参数。用Value注解可以将属性值直接注入到beans中。命令行参数以 -- 开头配置文件加载的顺…