字符串函数(二):strlen(求长度),strstr(查找子串),strtok(分割),strerror(打印错误信息)

字符串函数

  • 一.strlen(求字符串长度)
    • 1.函数使用
    • 2.模拟实现(三种方法)
  • 二.strstr(字符串查找子串)
    • 1.函数使用
    • 2.模拟实现
  • 三.strtok(字符串分割)
  • 四.strerror,perror(打印错误信息)

一.strlen(求字符串长度)

1.函数使用

size_t strlen(const char* str);
  • strlen函数用于求字符串的长度,参数是字符串的首地址,返回值是无符号的整形,初始化字符串有两种,它们各自存在一些小的细节,如下:
#include<stdio.h>
#include<string.h>
int main()
{
	char str1[] = "abcdef";//本质:{ 'a','b','c','d','e','f','\0' };隐藏了\0在末尾
	char str2[] = { 'a','b','c','d','e','f' };

	//函数strlen(): 求字符串长度
	printf("%zu\n", strlen(str1));//6
	printf("%zu\n", strlen(str2));//由于尾部没有'\0'随机数,所以打印随机数
	printf("%s\n", str2);//直到找到'\0'为止,停止打印

	//关键字sizeof():求字节的大小返回值同样是size_t(unsigned int)
	printf("%zu\n", sizeof(str1));//7
	printf("%zu\n", sizeof(str2));//6
	return 0;
}

在这里插入图片描述

  • 可以看到str2的长度居然是33,其实这是因为strlen函数会从首地址指向的字符一直向后查找,直到遇到’\0’,才会停下(不包含 ‘\0’ ),统计’\0’之前出现的字符的个数,而str2末尾没有’\0’,所以会一直向后查找,在某个位置恰好遇到了’\0’,所以打印随机数33。
  • printf函数打印字符串也是同样的道理,看似传入"abcdef",其实真正传入的是首地址(a的地址),遇到了’\0’,停止打印。那为什么会出现烫烫烫呢?这就牵扯到了函数栈帧了,日后会将函数栈帧更新上来。

总结:

  • 字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前⾯出现的字符个数(不包
    含 ‘\0’ )。

  • 参数指向的字符串必须要以 ‘\0’ 结束。

  • 注意函数的返回值为size_t,是无符号整形(size_t 等价于 unsigned int)。

易错点:

#include<stdio.h>
int main()
{
	if (strlen("abc") - strlen("abcdef"))
		printf(">\n");
	else
		printf("<\n");
	return 0;
}

在这里插入图片描述

  • 我们会发现输出的居然大于号,这是因为strlen函数的返回值是无符号的整形,而无符号的整形相减仍然得到无符号的整形,而-3按照无符号整形来说是一个很大的数,自然打印大于号。

如图:
在这里插入图片描述
在这里插入图片描述

  • 也可以这么写:
#include<stdio.h>
int main()
{
    //if ((int)strlen("abc") - (int)strlen("abcdef") >0)
	if (strlen("abc") > strlen("abcdef"))
		printf(">\n");
	else
		printf("<\n");
	return 0;
}

2.模拟实现(三种方法)

  • 方法一:计数器方法
  • 我们定义一个变量为count,如果传入的指针指向的内容不是’\0’,那么count++,同时指针后移一位,循环往复,直到找到’\0’时返回count即可。
#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);//断言,若str为NULL,报错,头文件assert.h
	size_t count = 0;
	while (*str != '\0')
	{
		str++;
		count++;
	}
	return count;
}
  • 方法二:指针减指针
  • 我们先定义两个指针变量start与end将传入的指针保存下来,然后将指针end向后移,直到遇到’\0’时,我们返回指针end与指针start的差值即可。(指针与指针的差的绝对值是两个指针之间的元素个数)。
#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);//断言,若str为NULL,报错,头文件assert.h
	const char* start = str;
	const char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end - start;
}
  • 方法三:递归
  • 判断传入指针指向的内容是否为’\0’,如果是就返回0,不是就返回my_strlen2(str+1)+1,如此进行下去,直到递归到内层时找到’\0’,这时再一步步将值返回回来即可。
#include<assert.h>
size_t my_strlen(const char* str)
{
    assert(str != NULL);//断言,若str为NULL,报错,头文件assert.h
	if (*str == '\0')
		return 0;
	else
		return my_strlen(str + 1) + 1;
}

二.strstr(字符串查找子串)

1.函数使用

char* strstr(const char* str1, const char* str2)
  • strstr函数用于在字符串 str1 中查找另一个字符串 str2,如果字符串 str1 存在字符串 str2,那么就返回字符串 str2 在字符串 str1 中第一次出现的起始位置,如果找不到那么就返回空指针(NULL)。它的第一个参数是字符串 str1 的首地址,第二个参数是字符串 str2 的首地址。
  • 若字符串 str2 为空字符串,则返回字符串 str1 的首地址。
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "ABCDEFG";
	char arr2[] = "CDE";
	char* ret = strstr(arr1, arr2);//返回arr1中字符C的地址
	if (ret != NULL)
		printf("%s\n", ret);//打印CDEFG
	else
		printf("子串不存在\n");
	return 0;
}

2.模拟实现

例如,在字符串"abbbcdef"中查找字符串"bbc":

  • p指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回指针p;当从该位置开始没有匹配成功时,则指针p后移一位进行下一次的匹配。
  • s1和s2指针: 通过判断s1和s2指针解引用后是否相等来判断单个字符是否匹配成功。若成功,则指针s1与s2后移一位比较下一对字符;若失败,指针p后移一位,指针s1返回指针p处,指针s2返回待查找字符串的起始位置。

如图:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
代码实现:

#include<assert.h>
#include<stdio.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	if (*str2 == '\0')//要查找的字符串为空字符串
	{
		return str1;
	}
	while(*p != '\0')//若要减少循环数则可以修改为
	//while ((p - str1) <= (int)strlen(str1) - (int)strlen(str2))
	{
		while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
	    {
			s1++;
			s2++;
		}
		if(*s2=='\0')//字符串查找子串成功
		{
			return p;
		}
		p++;
		s1 = p;
		s2 = str2;
	}
	return NULL;//找不到目标字符串,返回NULL
}
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("子串不存在\n");
	return 0;
}

对于以上代码,对于循环次数还可以优化下,比如待比较的字符串str2长度大于字符串str1还能比较的字符的个数时,是不可能查找到的,所以:

while(*p != '\0')//若要减少循环数则可以修改为
while ((p - str1) <= (int)strlen(str1) - (int)strlen(str2))

三.strtok(字符串分割)

char* strtok (char* str, const char* sep);

strtok函数能通过给定的一系列字符将一个字符串分割成许多子字符串的函数。它的第一个参数是需要被分割的字符串的首地址;第二个参数是一个字符串的首地址,该字符串是用作分隔符的字符集合。返回值是查找到的标记的首地址。

注意:

  1. sep参数指向一个字符串,定义了用作分隔符的字符集合。
  2. 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  3. strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回⼀个指向这个标记的指针。(注:
    strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串⼀般都是临时拷贝的内容并且可修改)
  4. strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  5. strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  6. 如果字符串中不存在更多的标记,则返回 NULL 指针。

例如:

#include<stdio.h>
#include<string.h>
int main()
{
	char email[] = "3315898248@qq.com";//待分割字符串
	char* sep = "@.";//分隔符的字符集合
	char cp[20] = { 0 };
	strcpy(cp, email);//将数据拷贝一份使用,防止原数据被修改

	//char* ret = strtok(email, sep);//第一次传参需传入待分割字符串首地址
	//while (ret != NULL)//说明还未分割完
	//{
	//	printf("%s\n", ret);
	//	ret = strtok(NULL, sep);//对同一个字符串进行分割,第二次及以后的第一个参数为NULL
	//}

	//或者
	char* ret = NULL;
	//利用for循环的特点,先ret = strtok(email, sep);判断ret != NULL;
	//打印ret,再ret = strtok(NULL, sep);判断ret != NULL;循环到ret == NULL结束
	for (ret = strtok(email, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}

四.strerror,perror(打印错误信息)

char* strerror (int errnum);

strerror函数可以把参数部分错误码转换为对应的错误信息,将错误信息字符串的首地址返回来。

注意:

  1. 在不同的系统和C语言标准库的实现中都规定了一些errno(错误码),一般是放在 errno.h 这个头文件中说明的。
  2. C语言程序启动的时候就会使用一个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中。
  3. 而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
#include<errno.h>
#include<string.h>
#include<stdio.h>
//我们打印一下0~10这些错误码对应的信息 
int main()
{
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%s\n", strerror(i));
	}
	return 0;
}

在这里插入图片描述
例如:打开一个文件(fopen函数表示:打开文件(读或写),当其执行成功时会返回文件的首地址,执行失败时会返回一个空指针(NULL)。)

int main()
{
	FILE* pf = fopen("test.txt", "r");//打开文件 ———— 读文件
	if (pf == NULL)//若为NULL,文件打开失败,下一步查看错误信息
	{
		printf("%s\n", strerror(errno));//将错误码转换成对应的错误信息
		perror("fopen");//直接打印错误信息
		return 1;
	}
	return 0;
}

在这里插入图片描述

  1. printf(“%s\n”, strerror(errno)) 等价于 perror(“”)
  2. perror(“fopen”):可以代替strerror(errno)且效果更好,加上(fopen)来明确错误来源于哪里。
  3. 程序执行会加上一个冒号。

创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!
在这里插入图片描述

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

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

相关文章

24点游戏679

题目描述&#xff1a; 给定一个长度为4的整数数组 cards 。你有 4 张卡片&#xff0c;每张卡片上都包含一个范围在 [1,9] 的 数字。您应该使用运算符 [, -, *, /] 和括号 ( 和 ) 将这些卡片上的数字排 列成数学表达式&#xff0c;以获得值24。你须遵守以下规则: &#xff08;1&…

AI大模型日报#0514:OpenAI GPT-4o震撼发布、我是如何赢得GPT-4提示工程大赛冠军的

导读&#xff1a;欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了今日要点以及每条资讯的摘要。《AI大模型日报》今日要点&#xff1a;OpenAI在春季新品发布会上推出全能模型GPT-4o及桌面App&#xff0c;颠覆科技界。GPT-4o…

Pytorch学习-引言

Pytorch相关链接 Pytorch官方网站 https://pytorch.org/ Pytorch的Github仓库 https://github.com/pytorch/pytorch Pytorch论坛 https://discuss.pytorch.org/ Pytorch离线下载包链接 https://download.pytorch.org/whl/torch_stable.html Pytorch学习视频推荐链接 http://【…

C++类与对象基础探秘系列(二)

目录 类的6个默认成员函数 构造函数 构造函数的概念 构造函数的特性 析构函数 析构函数的概念 析构函数的特性 拷贝构造函数 拷贝构造函数的概念 拷贝构造函数的特性 赋值运算符重载 运算符重载 赋值运算符重载 const成员 const修饰类的成员函数 取地址及const取地址操作…

C++系统编程篇——Linux初识(系统安装、权限管理,权限设置)

(1)linux系统的安装 双系统---不推荐虚拟机centos镜像&#xff08;可以使用&#xff09;云服务器/轻量级云服务器&#xff08;强烈推荐&#xff09; ①云服务器&#xff08;用xshell连接&#xff09; ssh root公网IP 然后输入password ①添加用户&#xff1a; addus…

如何去掉试卷答案,并打印出来

实际上&#xff0c;针对试卷答案的问题&#xff0c;一个简单而高效的方法是使用图片编辑软件中的“消除笔”功能。只需将试卷拍摄成照片&#xff0c;然后通过这一功能&#xff0c;就可以轻松擦除答案。虽然这种方法可能需要一些时间和耐心&#xff0c;但它确实为我们提供了一个…

增程SUV价格即将崩盘?买车一定要再等等!

文 | AUTO芯球 作者 | 雷歌​ 真是“离谱”啊&#xff0c;车圈真是逗比欢乐多&#xff0c; 我这两天看一个博主连续40多小时开车直播&#xff0c;充电口、油箱盖全部封死&#xff0c;全程视频直播没断过&#xff0c; 就为了测试这两天刚上市的星际元ET续航有多远。 另一个…

深入解析RedisJSON:在Redis中直接处理JSON数据

码到三十五 &#xff1a; 个人主页 JSON已经成为现代应用程序之间数据传输的通用格式。然而&#xff0c;传统的关系型数据库在处理JSON数据时可能会遇到性能瓶颈。为了解决这一问题&#xff0c;Redis推出了RedisJSON模块&#xff0c;它允许开发者在Redis数据库中直接存储、查询…

Flink最全文档

Flink架构&#xff1a; 分布式系统Flink&#xff0c;需要有效分配和管理计算资源才能执行流应用程序。它集成了所有常见的集群资源管理器&#xff0c;例如Hadoop Yarn&#xff0c;Apache Mesos&#xff0c;Kubernetes&#xff0c;但是也可以设置作为独立集群甚至库来运行。 分…

3ds Max与Maya不同之处?两者哪个更适合云渲染?

3ds Max 和 Maya 都是知名的3D软件&#xff0c;各有其特色。3ds Max 以直观的建模和丰富的插件生态闻名&#xff1b;Maya 则在动画和角色创作方面更为出色。两者都支持云渲染技术&#xff0c;能帮助用户在云端高效完成项目。 一、3ds Max和Maya之间的主要区别&#xff1a; 3ds…

如何在控制台应用程序里面托管ASP.NET Core WebApi + swashbuckle生成接口文档

目录 介绍项目结构运行效果新增引用新增文件介绍 本文讲解如何在控制台应用程序里面托管ASP.NET Core WebApi + swashbuckle生成接口文档 本文是上一篇文章的延续,如果你对这部分内容还不了解,建议先读上一篇文章:如何在控制台应用程序里面托管ASP.NET Core网站 项目结构 …

原子学习笔记3——点亮 LED

一、应用层操控设备的两种方式 应用层如何操控底层硬件&#xff0c;同样也是通过文件 I/O 的方式来实现&#xff0c;设备文件便是各种硬件设备向应用层提供的一个接口&#xff0c;应用层通过对设备文件的 I/O 操作来操控硬件设备&#xff0c;譬如 LCD 显示屏、串口、按键、摄像…

MVC 过滤器

MVC 过滤器常用有4种 Action过滤器&#xff08;IActionFilter&#xff09; 》 行为过滤器Result过滤器 &#xff08;IResultFilter&#xff09;》 视图过滤器 或 结果过滤器Exception过滤器&#xff08;IExceptionFilter&#xff09;》 异常过滤器Authorization过滤器&#xf…

OpenAI 发布了免费的 GPT-4o,国内大模型还有哪些机会?

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 这是今天在某乎看到一个问题&#xff1a;OpenAI 发完 GPT-4o&…

涨点神器:即插即用特征融合模块!超低参数,性能依旧SOTA

在写论文时&#xff0c;一些通用性模块可以在不同的网络结构中重复使用&#xff0c;这简化了模型设计的过程&#xff0c;帮助我们加快了实验的迭代速度。 比如在视觉任务中&#xff0c;即插即用的特征融合模块可以无缝集成到现有网络中&#xff0c;以灵活、简单的方式提升神经…

AIGC数字人视频创作平台,赋能企业常态化制作数字内容营销

随着数字人技术不断发展&#xff0c;AIGC、元宇宙等相关产业迅速发展&#xff0c;企业通过3D虚拟数字人定制&#xff0c;打造出专属的数字人作为企业与用户沟通的新桥梁。 作为3D、AI数字人技术服务商及方案提供商&#xff0c;广州虚拟动力一直致力于为各领域企业通过3D虚拟数字…

OpenAI春季更新:GPT-4o模型来了!!

昨晚OpenAI直播发布了春季更新&#xff0c;推出了GPT-4o模型。这个模型是继gpt-4-turbo之后官方定义的新旗舰模型&#xff0c;可以实时对音频、视觉和文本进行推理。 它真的让人觉得&#xff0c;AGI又更近一步了&#xff01;&#xff01; GPT-4o介绍 GPT-4o&#xff08;“o”…

前端工程化 - 快速通关 - ES6

目录 ES6 1.1 let 1.2 const 1.3解构 1.4链判断 1.5参数默认值 1.6箭头函数 1.7模板字符串 1.8Promise 1.9Async 函数 1.10模块化 ES6 ●ECMAScript&#xff08;ES&#xff09; 是规范、 JavaScript 是 ES 的实现 ●ES6 的第一个版本 在 2015 年 6 月发布&#xff0c…

相机模型,坐标变换,畸变

小孔成像模型 墨子就记录了小孔成像是倒立的。这从几何光学的角度是很好理解的&#xff1a;光沿直线传播&#xff0c;上方和下方的光线交叉&#xff0c;导致在成像平面位置互换。 小孔的大小有什么影响&#xff1f; 小孔越大&#xff0c;进光量变大了&#xff0c;但是成像平…