当前位置: 首页 > news >正文

字符串函数(二):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));//6printf("%zu\n", strlen(str2));//由于尾部没有'\0'随机数,所以打印随机数printf("%s\n", str2);//直到找到'\0'为止,停止打印//关键字sizeof():求字节的大小返回值同样是size_t(unsigned int)printf("%zu\n", sizeof(str1));//7printf("%zu\n", sizeof(str2));//6return 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");elseprintf("<\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");elseprintf("<\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.hsize_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.hconst 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.hif (*str == '\0')return 0;elsereturn 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);//打印CDEFGelseprintf("子串不存在\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);elseprintf("子串不存在\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. 程序执行会加上一个冒号。

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

相关文章:

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

字符串函数 一.strlen&#xff08;求字符串长度&#xff09;1.函数使用2.模拟实现&#xff08;三种方法&#xff09; 二.strstr&#xff08;字符串查找子串&#xff09;1.函数使用2.模拟实现 三.strtok&#xff08;字符串分割&#xff09;四.strerror&#xff0c;perror&#x…...

EUCR-30S电机保护器施耐德EOCR

​EOCR主要产品有电子式电动机保护继电器&#xff0c;电子式过电流继电器&#xff0c;电子式欠电流继电器&#xff0c;电子式欠电压继电器&#xff0c;其它保护和监视装置&#xff0c;电流互感器。 电器密集型设计 ■ 二个集成组装电流互感器 ■ 欠载保护&#xff08;空转保护…...

人工神经网络(科普)

人工神经网络&#xff08;Artificial Neural Network&#xff0c;即ANN &#xff09;&#xff0c;是20世纪80 年代以来人工智能领域兴起的研究热点。它从信息处理角度对人脑神经元网络进行抽象&#xff0c; 建立某种简单模型&#xff0c;按不同的连接方式组成不同的网络。在工程…...

宇宙(科普)

宇宙&#xff08;Universe&#xff09;在物理意义上被定义为所有的空间和时间&#xff08;统称为时空&#xff09;及其内涵&#xff0c;包括各种形式的所有能量&#xff0c;比如电磁辐射、普通物质、暗物质、暗能量等&#xff0c;其中普通物质包括行星、卫星、恒星、星系、星系…...

安防视频/视频汇聚系统EasyCVR视频融合云平台助力智能化酒店安防体系的搭建

一、背景需求 2024年“五一”假期&#xff0c;全国文化和旅游市场总体平稳有序。文化和旅游部6日发布数据显示&#xff0c;据文化和旅游部数据中心测算&#xff0c;全国国内旅游出游合计2.95亿人次。“五一”假期县域市场酒店预订订单同比增长68%&#xff0c;而酒店作为一个高…...

SpringCloudAlibaba:5.1Sentinel的基本使用

概述 简介 Sentinel是阿里开源的项目&#xff0c;提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。 官网 https://sentinelguard.io/zh-cn/ Sentinel的历史 2012 年&#xff0c;Sentinel 诞生&#xff0c;主要功能为入口流量控制。 2013-2017 年…...

SHELL-双重循环习题练习

1.99乘法表 #!/bin/bash #99乘法表for ((second1; second<9; second)) dofor ((first1; first<second; first))do echo -n -e "${first}*${second}$[first*second]\t" done echo done ######### 首先定义了一个外循环变量second&#xff0c;初始值为1&am…...

2024年为什么很多电商商家,都想涌入视频号,究竟是什么原因?

大家好&#xff0c;我是电商糖果 对电商有了解的朋友&#xff0c;在今年肯定发现一个现象&#xff0c;那就是很多商家对视频号比较青睐。 视频号究竟有何魔力&#xff0c;让越来越多的商家都想要入驻。 其实很简单&#xff0c;它让商家看到了市场。 视频号背后是谁&#xf…...

Google Gemma 2B 微调实战(IT科技新闻标题生成)

本文我将使用 Google 的 Gemma-2b 模型来微调一个基于IT科技新闻正文来生成对应标题的模型。并且我将介绍如何使用高度集成的训练框架来进行快速微调。 开始前 为了尽可能简化整个流程,我将使用 linux-cn 数据集[1]作为本次训练任务的训练数据。 模型选择使用 Gemma-2b[2],…...

RabbitMQ:深入理解高性能消息队列

RabbitMQ&#xff1a;深入理解高性能消息队列 文章目录 RabbitMQ&#xff1a;深入理解高性能消息队列前言一、RabbitMQ概述二、RabbitMQ的核心概念三、RabbitMQ的工作原理一、生产者发送消息二、交换机转发消息三、队列存储消息四、消费者接收并处理消息 四、RabbitMQ的使用场景…...

【北京迅为】《iTOP-3588开发板源码编译手册》-第4章 Android12/Linux设备树简介

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…...

C++ C# 贝塞尔曲线

二阶贝塞尔曲线公式 三阶贝塞尔曲线公式 C 三维坐标点 二阶到N阶源码 //二阶公式&#xff1a; FVector BezierUtils::CalculateBezierPoint(float t, FVector startPoint, FVector controlPoint, FVector endPoint) {float t1 (1 - t) * (1 - t);float t2 2 * t * (1 - t);…...

勒索软件漏洞?在不支付赎金的情况下解密文件

概述 在上一篇文章中&#xff0c;笔者对BianLian勒索软件进行了研究剖析&#xff0c;并且尝试模拟构建了一款针对BianLian勒索软件的解密工具&#xff0c;研究分析过程中&#xff0c;笔者感觉构建勒索软件的解密工具还挺有成就感&#xff0c;因此&#xff0c;笔者准备再找一款…...

实时“秒回”,像真人一样语音聊天,GPT-4o模型强到恐怖

今天凌晨OpenAl发布了 GPT-4o&#xff0c;这是一种新的人工智能模式&#xff0c;集合了文本、图片、视频、语音的全能模型。 能实时响应用户的需求&#xff0c;并通过语音来实时回答你&#xff0c;你可以随时打断它。还具有视觉能力&#xff0c;能识别物体并根据视觉做出快速的…...

Properties配置文件和源码

先对测试类进行get方法复写得到getReqType 判断caseinfo等于get时&#xff0c;就是get请求&#xff0c;反之就不是 这里的url和param都是xxx代替&#xff0c;如果直接写内容&#xff0c;每次都会请求 三目运算优化 为什么要用配置文件 test里时url,可以将ip和端口写在配置文…...

redis原生命令及项目使用

主动更新策略 缓存问题及解决 布隆过滤出现哈希冲突解决方案: 选择合适的哈希函数:布隆过滤器的性能和哈希函数的选择密切相关。选择高效、低碰撞率的哈希函数可以降低误判率。通常使用的哈希函数有 MurmurHash、FNV 等。 合理设置过滤器大小:过滤器的大小(位数组的大小)…...

使用VSCode生成代码、查询数据表

利用VSCode中百度生成 一、 1、输入内容&#xff1a;“给我生成一个student表&#xff0c;要求有id,createDate,userName,phone,age,sex,introduce的列信息,给我DDL与DML,5条信息都为中文信息&#xff0c;并且有一个userName必须等于张先生” 2、将生成的语句复制粘贴到数据库…...

使用 PXE+Kickstart 批量网络自动装机

前言&#xff1a; 正常安装系统的话使用u盘一个一个安装会非常慢&#xff0c;所以批量安装的技术就出来了。 一、 概念 PXE &#xff08;Preboot eXecute Environment&#xff0c;预启动执行环境&#xff09;是由 Intel 公司开发的技术&#xff0c;可以让计算机通过网络来启动…...

微信小程序交互增强:实现上拉加载、下拉刷新与一键返回顶部【代码示例】

微信小程序交互增强&#xff1a;实现上拉加载、下拉刷新与一键返回顶部【代码示例】 基础概念实现步骤与代码示例1. 下拉刷新2. 上拉加载更多3. 返回顶部 性能优化与安全考虑结语与讨论 在微信小程序的开发过程中&#xff0c;提供流畅的用户体验至关重要&#xff0c;其中上拉加…...

leetcode刷题指南

本文我将分享给大家一套我自己使用良久并觉得非常高效的 学习论&#xff0c;它可以运用到 Leetcode 上的刷题&#xff0c;也可以 generalize 到生活中涉及到学习以及记忆的方方面面。当然&#xff0c;本文将以 Leetcode 刷题为 case study 去进行讲解。 更具体一点, 我会教大家…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...