【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容
小编在写这篇博客时,经过了九一八,回想起了祖国曾经的伤疤,勿忘国耻,振兴中华!加油,逐梦少年!
前言
💓作者简介: 加油,旭杏,目前大二,正在学习C++,数据结构等👀
💓作者主页:加油,旭杏的主页👀⏩本文收录在:再识C进阶的专栏👀
🚚代码仓库:旭日东升 1👀
🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖
学习目标:
在上一篇博客中,我们已经充分的学习了指针的相关知识,那么这篇博客,我们来重点关注一下字符串函数,主要学习求字符串长度的函数,长度受限制的字符串函数,长度不收限制的字符串函数,以及字符串查找函数,最后再来介绍一下错误信息报告。
之后,我们会将一些函数进行模拟实现,再拓展一下其他内容,C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串类型通常放在常量字符串或者字符数组中,字符串常量适用于那些对它不做修改的字符串函数。所以这篇博客还是比较重要的。
学习内容:
通过上面的学习目标,我们可以列出要学习的内容:
- 求字符串长度的函数,以及手撕代码,模拟实现
- 长度受限制的字符串函数,同样手撕代码,模拟实现
- 长度不受限制的字符串函数,观看其源代码,了解原理
- 字符串查找函数,同样手撕代码,模拟实现
- 介绍一下错误信息报告
小编了解有些人是不会看学习目标,那么我就再说一遍:说起字符串,C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。常量字符串适用于那些对他不做修改的字符串函数。
一、求字符串长度的函数——strlen
1.1 函数介绍
在【初阶C语言3】特别详细地介绍函数中,小编已经介绍了如何查找库函数的参数和具体用法,在这里小编就不多说了。回归主题,我们来看strlen函数。
简单来说,strlen函数是求字符串长度的库函数,传递的参数是要求字符串长度的首元素地址即可。但是要注意的是strlen函数本质上统计的是字符串中 '\0' 之前的字符的个数。看下面的代码进行区分:
#include <stdio.h> #include <string.h> //别忘了引头文件 int main() {char arr1[] = "sdgeghkoe";char arr2[] = "sdgeg\0hkoe";printf("strlen(arr1) = %d\n", strlen(arr1));printf("strlen(arr2) = %d\n", strlen(arr2));return 0; }
在来看strlen函数返回值的特点,仔细看,在库函数中,strlen函数的返回值的类型是无符号整形(size_t)。怎么理解这个无符号整形呢?我们来看下面的一道题:
int main() {const char* str1 = "abcdfre"; //const修饰,使指针指向的字符串不能被修改const char* str2 = "fsfe";if (strlen(str2) - strlen(str1) > 0){printf("str2 > str1");}else{printf("str1 > str2");}return 0; }
这道题按照常理来讲,str2指向的字符串长度比str1指向的字符串长度小,所以这个运算的结果应该是str1 > str2,但为什么结果是str2 > str1,。
因为strlen函数的返回值是无符号整形,两个无符号整形进行相加减的时候是不会出现负数的情况,所以不论这道题的结果是什么,永远都是str2 > str1。
1.2 模拟实现一下strlen函数
模拟实现strlen函数有三种方法:一个是计数器、一个是递归的方法、一个是指针减指针的方法。下面,小编来一一为大家进行讲解:
1.2.1 计数器方法实现strlen函数
1.3 总结一下strlen函数的要点
二、长度不受限制的字符串函数
2.1 strcpy函数
2.1.1 函数介绍
strcpy——string copying,表示的是字符串拷贝的工作,如果我们不清楚,我们可以在网站上进行搜索strcpy函数。

上面的意思是: 将源头的字符串传给目的地的字符串中,包括 '\0'。在使用strcpy函数进行拷贝字符串的时候,源字符串必须要有 '\0'(其是strcpy函数拷贝结束的标志),否则程序将会打印出一些不必要的内容。请看下图进行解释:
基本用法:
//第一种写法 int main() {char arr1[20] = "xxxxxxxxx";char arr2[] = { 's','a','d','\0'};printf("%s\n", strcpy(arr1, arr2));return 0; } //第二种写法 int main() {char arr1[20] = "xxxxxxxxx";char arr2[] = "sad";printf("%s\n", strcpy(arr1, arr2));return 0; }
int main() {char arr1[20] = "xxxxxxxxx";char arr2[] = { 's','a','d' };printf("%s\n", strcpy(arr1, arr2));return 0; }
2.1.2 strcpy函数使用时出现的一些问题
问题一:目的地的字符数组的大小比源头的字符数组的大小要小
代码:
int main() {char arr1[] = { 0 };char arr2[] = "hello";printf("%s\n", strcpy(arr1, arr2));return 0; }结果:
虽然会弹出警告,但是strcpy函数还是会完成其使命,将源头的字符串拷贝到目的地的字符串中,因为strcpy函数是不受长度限制的函数。
问题二:将源头的字符串拷贝到常量字符串中
代码:
int main() {char* p = (char* )"feifhw";char arr2[] = "hello";printf("%s\n", strcpy(p, arr2));return 0; }结果:
2.1.3 总结一下strcpy函数的要点
- 源字符串必须以 '\0' 结束
- 在拷贝的过程中,会将源字符串中的 '\0' 拷贝到目标空间中
- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变,不能是常量字符串
2.1.4 模拟实现一下strcpy函数
char* my_strcpy(char* base, const char* serc) //源头的内容不用改,用const修饰
{assert(base && serc); //检查指针是否指空char* ans = base;while (*base++ = *serc++){;}return ans;
}
2.2 strcat函数
2.2.1 函数介绍
这个函数是用于字符追加的,如果我们不清楚,我们依然可以进行在网站上查询strcat函数。下面小编我将会为大家用代码示范一下如何使用strcat函数:
基本用法:
int main() {char arr1[20] = "asd";char arr2[] = "hjk";printf("%s\n", strcat(arr1, arr2));return 0; }
在上面的用法中,我们好像已经浅浅地了解了strcat函数的用法,就是将源头的字符串追加到目的地的字符串中。那么这时,我们要来思考一下问题:1)到底是遇见 '\0' 进行追加,还是遇见字符串末尾进行追加呢?2)字符串在拷贝的过程中是否将源头的 '\0' 也拷贝过去呢?3)目的地字符串数组是否要足够大?下面我们来解决一下这些问题:
2.2.2 解决上述strcat函数的相关问题
问题一:到底是遇见什么进行追加(遇见'/0'进行追加)
问题二:在拷贝的过程中将源头的'\0'拷贝过去
int main() {char arr1[20] = "asdxxx\0xxxxx";char arr2[] = "hjk";printf("%s\n", strcat(arr1, arr2));return 0; }
问题三:目的地数组要足够大(与strcpy类似)
int main() {char arr1[] = "asdxxx";char arr2[] = "hjk";printf("%s\n", strcat(arr1, arr2));return 0; }
2.2.3 总结一下strcat函数的要点
- 源字符串必须要以 '\0' 结束,保证能够找到目标空间的末尾
- 目标变量必须要足够大,能容纳下源字符串的内容
- 目标函数必须可以修改
2.2.4 模拟实现一下strcat函数
char* my_strcat(char* str1, const char* str2)
{assert(str1 && str2);char* ret = str1;//找到目标空间的末尾while (*str1 != 0){str1++;}//数据追加while (*str1++ = *str2++){;}return ret;
}
2.2.5 思考一下字符串能和自己追加呢?
显然是不能的,因为如果自己给自己进行追加,会导致字符串的内容发生一些变化,将字符0给堵盖掉,就没有结束标志了,看下图进行理解:

2.3 strcmp函数
2.3.1 函数介绍
strcmp——string compare,表示的是字符串比较的工作,注意这个字符串函数比较的不是字符串的大小,而是对应位置上字符的大小(ASCII值)。我们也可以在网站上搜索strcmp函数。

标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
要注意的是,这个函数的返回值为int,两个参数都是const char*类型的,因为我只是想要比较两个字符串的大小,不需要进行修改。
基本用法:
int main() {char arr1[] = "dsgfer";char arr2[] = "dsgfty";printf("%d\n", strcmp(arr1, arr2));return 0; }
2.3.2 模拟实现一下strcmp函数
//第一种写法,返回值是1和-1
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str2 == 0)return 0;str1++;str2++;}if (*str1 < *str2)return -1;else if(*str1 > *str2)return 1;
}//第二种写法,返回值不一定是1和-1
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 && *str2 && *str1 == *str2){str1++;str2++;}return *str1 - *str2;
}
2.4 长度不受限制的字符串函数的特点
这些字符串函数都会将直接完成该完成的任务,不会管目的地字符串数组能否放的下,它都会将放入其中,这些函数是简单粗暴的,而我们学的下几个函数不会像这几个函数一样。
三、长度受限制的字符串函数
我们来看这几个函数strncpy,strncat,strncmp,它们都多了一个n,也就是number。这些函数都会确定几个字符进行该函数的工作,所以说,它们是长度受限制的字符串函数。
3.1 strncpy函数
3.1.1 函数介绍
在和strcpy函数进行比较时,会发现参数部分多了一个num,要和strcpy进行区分一下,所以其作用是拷贝num个字符从源字符串到目标空间中。我们依然可以在网站上搜索strncpy函数。

3.1.2 strncpy函数要注意的要点
要点一:是否在后面拷贝 '\0' ?
这个函数并没有在后拷贝 '\0' ,可以看下面的代码:
int main() {char arr1[] = "xxxxxxxxxxx";char arr2[] = "asd";printf("%s\n", strncpy(arr1, arr2, 3));return 0; }
要点二:如果源字符串的长度小于num,怎么办?
如果源字符串的长度小于num,则在拷贝完字符串之后,在目标的后面追加0,直到num个。
int main() {char arr1[] = "xxxxxxxxxxx";char arr2[] = "asd";printf("%s\n", strncpy(arr1, arr2, 6));return 0; }
3.2 strncat函数
3.2.1 函数介绍
这个函数和strcat函数的功能是一样的,我们同样可以在网站上搜索strncat函数。

基本用法:
int main() {char arr1[20] = "hoojp";char arr2[20] = "wqoieh";strncat(arr1, arr2, 4);printf("%s\n", arr1);return 0; }
3.2.2 strncat函数要注意的要点
我们要记住strncat函数将几个字符追加到另一个字符串中,会在后面添加一个 '\0'。看下面的代码:
int main() {char arr1[20] = "hoojp\0xxxxx";char arr2[20] = "wqoieh";strncat(arr1, arr2, 4);printf("%s\n", arr1);return 0; }
3.3 strncmp函数
该函数的功能是比较到出现另一个字符不一样或者一个字符串结束或者num个字符全部比较完。看下图的精确含义:

基本用法:
int main() {char arr1[20] = "hjoihsoad";char arr2[20] = "hjoihjdkp";int ret = strncmp(arr1, arr2, 5);printf("%d\n", ret);return 0; }
四、字符串查找函数
这些函数还是比较怪的,不同于前面讲述的三类函数,是一下关于字符串内容的操作,我们来一起看看吧!
4.1 strstr函数
4.1.1 函数介绍
strstr——string string,是在字符串中查找子字符串中的操作,根据函数原型,可以发现是在str1中找到str2第一次出现的位置,如果str1中没有str2,就返回NULL。

基本用法:
int main() {char str[] = "This is a simple string";char* pch;pch = strstr(str, "simple");printf("%s", pch);return 0; }
4.1.2 模拟实现一下strstr函数
const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);const char* s1;const char* s2;const char* cp;cp = str1;while (*cp){s1 = cp;s2 = str2;while (*s1 == *s2){if (*s2 == 0)return cp;s1++;s2++;}cp++;}return NULL;
}
4.1.3 拓展一下kmp算法
4.2 strtok函数
strtok函数是用来切割字符串的,如果在一个字符串中有分隔符的话,我们可以使用这个函数去按照分隔符将一个字符串分割成好几份。下面来看这个函数的参数:

- sep参数是一个字符串,定义了用作分隔符的字符的集合;
- 第一个参数制定一个字符串,它包含了0个或者多个由字符串中的一个或者多个分隔符分割的标记;
- strtok函数找到str中的下一个标记,并将其用 '\0' 进行结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改)
- strtok函数的一个参数不为NULL,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置;
- strtok函数的一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记;
- 如果字符串中不存在更多标记,则返回NULL指针。
基本用法:
int main() {char arr[] = "shdakio@iahd.isauh";char buf[200] = { 0 };strcpy(buf, arr);const char* p = "@.";char* s = NULL;for (s = strtok(arr, p); s != NULL; s = strtok(NULL, p)){printf("%s\n", s);}return 0; }
五、错误信息报告函数strerror
strerror函数是将错误码翻译成错误信息,返回错误信息的字符串的起始位置。错误码有是什么,C语言在使用库函数时,如果发生错误,会将错误码放在errno中,errno是全局变量,可直接使用。
基本用法:
int main() {int i = 0;for (int i = 0; i < 10; i++){printf("%s\n", strerror(i));}return 0; }
学习产出:
- 求字符串长度的函数,以及手撕代码,模拟实现
- 长度受限制的字符串函数,同样手撕代码,模拟实现
- 长度不受限制的字符串函数,观看其源代码,了解原理
- 字符串查找函数,同样手撕代码,模拟实现
- 介绍一下错误信息报告
相关文章:
【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容
小编在写这篇博客时,经过了九一八,回想起了祖国曾经的伤疤,勿忘国耻,振兴中华!加油,逐梦少年! 前言 💓作者简介: 加油,旭杏,目前大二,…...
docker启动mysql8目录挂载改动
5.7版本: 拉取mysql镜像 docker pull mysql:5.7启动 docker run -p 3306:3306 --name mysql5 \ -v /Users/zhaosichun/data/dockerData/log:/var/log/mysql \ -v /Users/zhaosichun/data/dockerData/data:/var/lib/mysql \ -v /Users/zhaosichun/data/dockerData…...
CHATGPT中国免费网页版有哪些-CHATGPT中文版网页
CHATGPT中国免费网页版,一个强大的人工智能聊天机器人。如果你曾经感到困惑、寻求答案,或者需要一些灵感,那么CHATGPT国内网页版可能会成为你的好朋友。 CHATGPT国内免费网页版:你的多面“好朋友” 随着人工智能技术的不断发展&a…...
docker network create命令
docker network create命令用于创建一个新的网络连接。 DRIVER接受内置网络驱动程序的桥接或覆盖。如果安装了第三方或自己的自定义网络驱动程序,则可以在此处指定DRIVER。 如果不指定--driver选项,该命令将为您自动创建一个桥接网络。 当安装Docker Eng…...
4G版本云音响设置教程腾讯云平台版本
文章目录 4G本云音响设置教程介绍一、申请设备三元素1.腾讯云物联网平台2.创建产品3.设置产品参数4.添加设备5.获取三元素 二、设置设备三元素1.打开MQTTConfigTools2.计算MQTT参数3.使用USB连接设备4.设置参数 三、腾讯云物联网套件协议使用说明1.推送协议信息2.topic规则说明…...
Grafana离线安装部署以及插件安装
Grafana是一个可视化面板(Dashboard),有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持Graphite、zabbix、InfluxDB、Prometheus和OpenTSDB作为数据源。Grafana主要特性:灵活丰富的图形…...
非独立随机变量的概率上界估计
目前的概率论或者随机变量书籍过分强调对独立随机变量的大数定律,中心极限定理,遗憾上界的估计。而对于非独立随机变量的研究很少,在《概率论的极限定理》中曾给出过一般随机变量求和的渐进分布簇的具体形式,然而形式却太过复杂。…...
常见电子仪器及其用途
常见电子仪器及其用途包括: 示波器:示波器是一种用途十分广泛、易于使用且功能强大的电子测量仪器。它能把肉眼看不见的电信号变换成看得见的图像,便于我们研究各种电现象的变化过程。示波器可以直接用来测量电信号的波形,是电子…...
配置测试ip、正式ip、本地ip
目的:npm run serve启动本地服务,npm run test打包测试环境,npm run build打包正式环境。 具体做法如下: 一、在项目中新增三个环境的文件 .env.development VITE_BASE_URLhttp://192.168.1.12:8080/ .env.production VITE_…...
Linux 系统移植(一)-- 系统组成
参考资料: linux系统移植篇(一)—— linux系统组成【野火Linux移植篇】1-uboot初识与编译/烧录步骤 文章目录 一、linux系统组成二、Uboot三、Linux内核四、设备树 本篇为Linux系统移植系列的第一篇文章,介绍了一个完整可运行的L…...
利用git的贮藏功能
可以将自己分支的当前状态贮藏切换到其它分支再切换回来的时候,应用就行了...
第52节:cesium 3DTiles模型特效+选中高亮(含源码+视频)
结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><vc-navigation...
day03_基础语法
今日内容 零、复习昨日 一、Idea安装,配置 二、Idea使用 三、输出语句 四、变量 五、数据类型 附录: 单词 零、 复习昨日 1 装软件(typora,思维导图) 2 gpt(学会让他帮你解决问题) 3 java发展(常识) 4 HelloWorld程序 5 编码规范 6 安装jdk,配置环境变量 电脑常识 任…...
数据结构与算法-时间复杂度与空间复杂度
数据结构与算法 🎈1.概论🔭1.1什么是数据结构?🔭1.2什么是算法? 🎈2.算法效率🔭2.1如何衡量一个算法的好坏?🔭2.2算法的复杂度🔭2.3时间复杂度📖2…...
数组的去重
根据您提供的代码片段,看起来您尝试使用嵌套的 for 循环将数组 data 中的元素添加到新数组 newData 中。然而,在您给出的代码中,if 语句的条件部分为空,可能是因为您还没有确定用于判断重复项的条件。如果您想要去除数组中的重复项…...
Electron自动化测试技术选型调研
Electron简介 Electron是一个开源的框架,用于构建跨平台的桌面应用程序。它由GitHub开发并于2013年首次发布。Electron允许开发人员使用Web技术(如HTML、CSS和JavaScript)来构建桌面应用程序,同时可以在Windows、macOS和Linux等操…...
微服务学习(九):安装OpenOffice
微服务学习(九):安装OpenOffice 一、下载OpenOffice 下载地址:OpenOffice 二、开始安装 上传资源到服务器 解压资源包 tar -zxvf Apache_OpenOffice_4.1.13_Linux_x86-64_install-rpm_zh-CN.tar.gz进入zh-CN/RPMS目录下安装…...
SAP Oracle表空间扩展技术手册
1、DBACOCKPIT下查看表空间 当表空间不足(达到99%)时,需要按以下步骤扩充表空间(每次扩充20000M,20G): (也可以通过DB13,DB02查看表空间) 新浪博客 Tablespace PSAPSR3 is 100% used | SAP Community Oracle是通过增加数据文件的方式来为表空间扩容。为指定表空间增…...
Linux系统编程——线程的学习
学习参考博文: Linux多线程编程初探 Linux系统编程学习相关博文 Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——进程间通信的学习Linux系统编程——网络编程的学习 Linux系统编程——线程的学习 一、概述1. 进程与线程的区别2. 使…...
zemaxMIF曲线图
调制传递函数( Modulation Transfer Function,MTF )是用来形容光学系统成像质量的重要指标。 通过对光学系统像空间进行傅里叶变换,可以得到一张分析图表,来描述像面上对比度和空间频率之间的对应关系。 对比度&…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

















