【C语言基础】:字符函数和字符串函数

文章目录

    • 一、字符函数
      • 1. 字符分类函数
      • 2. 字符转化函数
    • 二、字符串函数
      • 1. strlen函数的使用和模拟实现
        • strlen函数的使用
        • strlen函数的模拟实现
      • 2. strcpy函数的使用和模拟实现
        • strcpy函数的使用
        • strcpy函数的模拟实现
      • 3. strcat函数的使用和模拟实现
        • strcat函数的使用
        • strcat函数的模拟实现
      • 4. strcmp函数的使用和模拟实现
        • strcmp函数的使用
        • strcmp函数的模拟实现

一、字符函数

1. 字符分类函数

C语言中有一系列的函数是对字符进行分类的,就是对判断一个字符属于什么类型的字符,这类字符函数的使用都要包含一个头文件ctype.h

在这里插入图片描述
这些函数的使用方法非常类似,这里我们就只举一个例子。

 int islower ( int c );

在C语言中,islower是一个用于判断字符是否为小写字母的函数。该函数接受一个整数参数c,它应该是无符号字符或EOF(常量)。如果参数c是小写字母,则返回非零值(真);否则返回0(假)。

【示例】将字符串中的小写字母转大写,其他字符不变。

#include<stdio.h>
#include<ctype.h>
int main()
{char str[] = "Test String.\n";int i = 0;char c;while (str[i]){c = str[i];if (islower(c))c -= 32;  // 小写字母的ASCII码值减去32就等于它对应的大写字母的ASCII码putchar(c);i++;}return 0;
}

在这里插入图片描述

2. 字符转化函数

C语言提供了2个字符转换函数:

int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写 
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写

在上面的示例中,我们将小写转大写,是-32完成的效果,有了转换函数,就可以直接使用 tolower 函
数。

#include<stdio.h>
#include<ctype.h>
int main()
{char str[] = "Test String.\n";int i = 0;char c;while (str[i]){c = str[i];if (islower(c))c = toupper(c);putchar(c);i++;}return 0;
}

在这里插入图片描述

二、字符串函数

1. strlen函数的使用和模拟实现

函数原型:

size_t strlen ( const char * str );
  • 字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前⾯出现的字符个数(不包含 ‘\0’ )。
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为 size_t,是无符号的( 易错 )
  • strlen的使用需要包含头文件
  • 学会strlen函数的模拟实现
strlen函数的使用

计算字符串的长度

#include<stdio.h>
#include<string.h>
int main()
{char str1[] = "abcdef";size_t len = strlen(str1);printf("%zu\n", len);return 0;
}

在这里插入图片描述

strlen函数的模拟实现

方法一:计数器的方式实现

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)  // const修饰,使其不能被修改
{int count = 0;assert(str);  // 断言,避免传入空指针while (*str){count++;  // 进入循环,说明不为空str++;}return count;
}
int main()
{char str1[] = "abcdef";int len = my_strlen(str1);printf("%d\n", len);return 0;
}

在这里插入图片描述
方法二:递归实现

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str1)
{assert(str1);   // 断言,避免传入空指针if (*str1 == '\0')return 0;  // 等于'\0',说明字符串结束,直接返回elsereturn 1 + my_strlen(str1 + 1);
}
int main()
{char str1[] = "abcdef";int len = my_strlen(str1);printf("%d\n", len);return 0;
}

在这里插入图片描述
方法三:指针 - 指针的方式实现

#include<stdio.h>
int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s;
}
int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
}

在这里插入图片描述

2. strcpy函数的使用和模拟实现

函数原型:

char* strcpy(char * destination, const char * source );

将source指向的C字符串复制到destination指向的数组中,包括结束的null字符(并在该点停止)。

strcpy函数的使用

将str1中的内容拷贝到str2中去

#include<stdio.h>
#include<string.h>
int main()
{char str1[] = "hello world";char str2[20] = { 0 };strcpy(str2, str1);  // 将str1中的内容拷贝到str2中去printf("%s\n", str2);return 0;
}

在这里插入图片描述
注意:这里str2的空间必须得能够装下str1中的内容,也就是str2的空间要足够大

strcpy函数的模拟实现

在模拟实现之前,首先我们要了解strcpy的实现原理,只有这样才会更加方便我们去模拟实现。那么,strcpy的实现原理是怎样的呢?
这里我们根据上面的代码进行修改再调试一下:

#include<stdio.h>
#include<string.h>
int main()
{char str1[] = "hello world";char str2[20] = "xxxxxxxxxxxxxxxxxx";strcpy(str2, str1);printf("%s\n", str2);return 0;
}

str1加上空格和字符串的结束标志 ‘\0’ 一共有12个字符,如果在str2中第12(下标为11)个字符也变成了 ‘\0’,那就说明是把str1中的 ‘\0’ 搬到了str2中。
在这里插入图片描述
事实证明strcpy的确是这样实现字符串拷贝的,所以我们在模拟实现时要一直将字符串拿到 ‘\0’ 才能结束。
注意

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可修改。

接下来我们来一步一步模拟strcpy函数

void my_strcpy(char* dest, char* src)
{while (*src != '\0')  // 拷贝'\0'之前的内容{*dest = *src;dest++;src++;}*dest = *src;  // 拷贝'\0'
}

这样其实也能模拟实现,但这样的代码明显还有很多缺陷,还有很多优化的空间。

char* my_strcpy(char* dest, const char* src)
{assert(dest != NULL);assert(src != NULL);char* ret = dest;while (*dest++ = *src++);return ret;
}

这是最终优化后的代码,大家看到这里可能会有点懵,别急,我们慢慢来:

  1. 首先为了确保安全,我们得判断传入进来的dest和src是否为空指针,所以这里要断言一下,一旦他们为空指针就直接报错。
  2. 其次,为了确保src不被修改,可以用const进行修饰。在while循环*dest++ = *src++作为判断条件,这里因为++的优先级高于 * ,但他是后置++,先使用再自增,所以也就是先解引用再++。
  3. 第一次判断会将src中的第一个字符给dest,因为while循环里的是空语句,执行了也不会有任何效果,然后就这样一直判断,一直将src中的字符给到dest,知道将’\0’给到dest,这时whlie循环括号里的表达式为0,跳出循环,但src中包括’\0’全部给到了dest。
  4. 因为原strcpy函数的返回值是char * 类型的,我们这里为了模拟也改成char * 类型的,原本我们是要返回dest的,但由于在while循环里面dest已经后置++不在指向首地址,所以在这之前我们先用一个指针ret来存放dest的首地址,最后我们直接返回ret就行了。
#include<assert.h>
#include<stdio.h>
#include<ctype.h>
char* my_strcpy(char* dest, const char* src)
{assert(dest != NULL);assert(src != NULL);char* ret = dest;while (*dest++ = *src++);return ret;
}
int main()
{char str1[] = "hello world";char str2[20] = "xxxxxxxxxxxxxxxxxx";my_strcpy(str2, str1);printf("%s\n", str2);char * ret = my_strcpy(str2, str1);printf("%s\n", ret);return 0;
}

在这里插入图片描述

3. strcat函数的使用和模拟实现

函数原型:

char * strcat ( char * destination, const char * source );

连接字符串
将原字符串的副本追加到目标字符串。destination中的结束null字符被source的第一个字符覆盖,并且在destination中由两者串联形成的新字符串的末尾包含一个空字符。

  • 源字符串必须以 ‘\0’ 结束。
  • ⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。
  • ⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。
  • ⽬标空间必须可修改。
  • 字符串⾃⼰给⾃⼰追加,如何?
strcat函数的使用

将arr2中的字符串追加到arr2后面去。

#include<stdio.h>
#include<string.h>
int main()
{// 注意数组空间大小,要足以容纳追加后的字符数量char arr1[20] = "hello ";  char arr2[] = "world";strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

在这里插入图片描述
注意:这里arr1的空间必须得能够装下arr2追加到arr1后中的内容,也就是arr1的空间要足够大。

strcat函数的模拟实现

和之前一样,在模拟实现之前我们先要了解strcat函数的实现原理。这里我们同样调试一下看看。
在这里插入图片描述
调试之后可以看到,追加的过程是从arr1中的 \0开始追加,但是arr2中的 \0并没有拷贝到arr1中。
根据strcat函数的实现原理我们来进行模拟实现:

#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{assert(dest && src);  // 断言判断传入的是否为空指针char* ret = dest;  // 记录dest的起始地址// 找到目标空间的\0while (*dest != '\0')dest++;// 拷贝追加while (*dest++ = *src++);return ret;
}
int main()
{// 注意数组空间大小,要足以容纳追加后的字符数量char arr1[20] = "hello ";  char arr2[] = "world";// my_strcat(arr1, arr2);char* ret = my_strcat(arr1, arr2);printf("%s\n", arr1);printf("%s\n", ret);return 0;
}

在这里插入图片描述
和上面一样,调试之后我们知道被追加的字符串从 \0开始,但追加的字符串并不会将 \0给追加进来。

  1. 首先我们肯定要判断传入进来的指针是否为空指针,这里要断言一下,而且为了防止要追加的字符串被修改,我们要将它用const修饰一下。
  2. 因为被追加的字符串是从 \0开始追加的,所以我们在追加之前要将指针指向字符串结尾,这里用while循环判断,只要不是\0我们就加一,直到指向字符串结尾。
  3. 这里就和strcpy的模拟一样了,也是在while循环的条件那里判断*dest++ = *src++,只要 *src没到\0,那么这个表达式的结果就不会为0,循环就还会继续,知道\0为止。
  4. strcat函数原型的返回值的char * 类型的,这里我们也返回char * 类型的,但要注意这里返回的是被追加字符串的起始地址,由于我们在实现追加的过程中将指针往后进行了偏移不在指向起始地址,所以我们要创建一个指针变量来记录被追加字符串的起始地址,最后返回这个创建的指针变量就行了。

4. strcmp函数的使用和模拟实现

函数原型:

int strcmp ( const char * str1, const char * str2 );

比较两个字符串
比较C字符串str1和C字符串str2。这个函数执行字符的二进制比较。
这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续执行以下对,直到字符不同或达到终止空字符为止。

  • 标准规定:
    ◦ 第⼀个字符串大于第⼆个字符串,则返回大于0的数字
    ◦ 第⼀个字符串等于第⼆个字符串,则返回0
    ◦ 第⼀个字符串小于第⼆个字符串,则返回小于0的数字
    ◦ 那么如何判断两个字符串? 比较两个字符串中对应位置上字符ASCII码值的大小。
strcmp函数的使用

比较三个字符串

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdef";char arr2[] = "abd";char arr3[] = "abcdef";int ret1 = strcmp(arr1, arr2);int ret2 = strcmp(arr1, arr3);int ret3 = strcmp(arr2, arr3);printf("%d\n", ret1);printf("%d\n", ret2);printf("%d\n", ret3);return 0;
}

在这里插入图片描述
在这里插入图片描述
可以看到,标准里面的返回值是一个大于或小于0的数,但VS的编译器直接定为1和-1,其他编译器可能是返回一个大于0或小于0的数。

strcmp函数的模拟实现
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0')return 0;str1++;str2++;}if (*str1 > *str2)return 1;elsereturn -1;
}
int main()
{char arr1[] = "abcdef";char arr2[] = "abd";char arr3[] = "abcdef";int ret1 = my_strcmp(arr1, arr2);int ret2 = my_strcmp(arr1, arr3);int ret3 = my_strcmp(arr2, arr3);printf("%d\n", ret1);printf("%d\n", ret2);printf("%d\n", ret3);return 0;
}

在这里插入图片描述
模拟后发现结果是一样的。

  1. 还是和上面一样,因为函数中我们只是比较字符串,不需要进行修改,所以要对其进行const修饰,而且为了避免传入空指针,要对其进行断言处理。
  2. while循环中进行条件判断,相等就进入循环,并自增,不相等跳出循环,跳出循环后又分两种情况,一种是 *str1 > *str2,直接返回1,*str1 < *str2,直接返回-1。
  3. 相等的情况只可能出现在while循环中,因为不相等就直接跳出循环了,一旦当 *str1 == \0,也就说明 *str2也等于\0了,直接返回0就行了。

注意:这只是在模拟VS编译器里的结果,模拟其他编译器上的结果更加简单,不相等时直接返回他们对应的ASCII码的差值就行了。

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0')return 0;str1++;str2++;}return *str1 - *str2;}
int main()
{char arr1[] = "abcdef";char arr2[] = "abs";char arr3[] = "abcdef";int ret1 = my_strcmp(arr1, arr2);int ret2 = my_strcmp(arr1, arr3);int ret3 = my_strcmp(arr2, arr3);printf("%d\n", ret1);printf("%d\n", ret2);printf("%d\n", ret3);return 0;
}

在这里插入图片描述

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

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

相关文章

el-table树形数据序号排序处理

1&#xff0c;用下面这个代码可以实现基本表格的序号排序 <el-table-column label"序号" width"50px" align"center"><template slot-scope"scope">{{ scope.$index 1 }}</template></el-table-column>2&…

【LeetCode热题100】104. 二叉树的最大深度(二叉树)

一.题目要求 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 二.题目难度 简单 三.输入样例 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&am…

训练数据集(一):真实场景下采集的煤矸石目标检测数据集,可直接用于YOLOv5/v6/v7/v8训练

文章目录 数据集介绍数据集训练精度展示数据集获取方式 数据集介绍 煤矸石训练数据集&#xff1a;891张&#xff1b;验证数据数据集&#xff1a;404张 数据集类别&#xff1a;0代表煤炭&#xff08;coal&#xff09;&#xff0c;1代表矸石&#xff08;gangue&#xff09;&…

Git reset命令后如何恢复到最新版本

文章目录 Git reset命令后如何恢复到最新版本使用git reflog命令使用git checkout命令 总结 Git reset命令后如何恢复到最新版本 Git reset命令后&#xff0c;可以使用以下两种方法恢复到最新版本&#xff1a; 使用git reflog命令 git reflog该命令可以查看所有Git操作的记录…

【C语言】比较两个字符串大小,strcmp函数

目录 一&#xff0c;strcmp函数 1&#xff0c;strcmp函数 2&#xff0c;函数头文件&#xff1a; 3&#xff0c;函数原型&#xff1a; 4&#xff0c;返回取值&#xff1a; 二&#xff0c;代码实现 三&#xff0c;小结 一&#xff0c;strcmp函数 1&#xff0c;strcmp函数 …

docxTemplater——从word模板生成docx文件

官网文档&#xff1a;Get Started (Browser) | docxtemplater 官网在线演示&#xff1a;Demo of Docxtemplater with all modules active | docxtemplater 源码&#xff1a;https://github.com/open-xml-templating/docxtemplater 不仅可以处理word&#xff08;免费&#xf…

【ArcGIS 脚本工具】强制移动要素类,绕过空间参考不一致

作为一个合格的数据管家&#xff0c;自然要让自己的数据库井井有条。 于是想着整理一下数据库里面的七零八落的要素类&#xff0c;按 数据库-要素数据集-要素类 的方式整理。 但是将要素类移动到要素数据集内的时候经常会出现下面的报错。 这大概率是因为要素类的坐标系与目标…

使用ChatGPT高效完成简历制作[中篇2]-有爱AI实战教程(九)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、导读&#xff1a; 在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xff0c…

0301taildir-source报错-flume-大数据

1 基础环境简介 linux系统&#xff1a;centos&#xff0c;前置安装&#xff1a;jdk、hadoop、zookeeper、kafka&#xff0c;版本如下 软件版本描述centos7linux系统发行版jdk1.8java开发工具集hadoop2.10.0大数据生态基础组件zookeeper3.5.7分布式应用程序协调服务kafka3.0分…

【Docker】使用Docker部署IT运维管理平台CAT

作者怀揣着一个美好的愿景&#xff0c;旨在提升管理效率、推动开源项目的蓬勃发展。 来一杯咖啡与茶&#xff0c;为 IT 运维从业者减轻管理负担&#xff0c;提升管理效率&#xff0c;从繁重无序的工作中解压出来&#xff0c;利用剩余时间多喝一杯休息一下。 这是一个专为 IT 运…

windows安装prometheus和grafana

prometheus官网 grafana 软件下载 prometheus windows_exporter https://github.com/prometheus-community/windows_exporter grafana prometheus原理 配置prometheus 解压之后prometheus-2.50.1.windows-amd64.zip修改prometheus.yml localhost修改为127.0.0.1 双击p…

爬虫神器!使用Python一键下载网页图片,省时高效!

引言 爬虫技术在当今信息时代中扮演着重要的角色&#xff0c;可以自动化获取互联网上的数据。本教程将围绕你提供的Python爬虫代码展开&#xff0c;旨在实现自动下载图片的功能。通过这个示例&#xff0c;你将学习如何利用爬虫技术批量获取网页中的图片&#xff0c;并将其保存…

Python下有关CV的一些算法和函数

目录&#xff1a; 1. HoughCircles二级目录三级目录 1. HoughCircles 霍夫圆检测 二级目录 三级目录

Python轴承故障诊断 (17)基于TCN-CNN并行的一维故障信号识别模型

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Python轴承故障诊断 (一)短时傅里叶变换STFT Python轴承故障诊断 (二)连续小波变换CWT_pyts 小波变换 故障-CSDN博客 Python轴承故障诊断 (三)经验模态分解EMD_轴承诊断 …

docker 安装minio,详细图解

废话不多说&#xff0c;直接上干货 docker 安装minio 拉取镜像 docker pull minio/minio创建数据目录、配置目录 mkdir /opt/minio/data mkdir /opt/minio/config启动容器 docker run -p 9000:9000 -p 9090:9090 \--name minio \-d --restartalways \-e "MINIO_ACCESS_KE…

[LLM]大语言模型文本生成—解码策略(Top-k Top-p Temperature)

{"top_k": 5,"temperature": 0.8,"num_beams": 1,"top_p": 0.75,"repetition_penalty": 1.5,"max_tokens": 30000,"message": [{"content": "你好","role": "user&…

【递归专题】【蓝桥杯备考训练】:有序分数、正则问题、带分数、约数之和、分形之城【已更新完成】

目录 1、有序分数&#xff08;usaco training 2.1&#xff09; 2、正则问题&#xff08;第八届蓝桥杯省赛C A组 & Java A组&#xff09; 3、带分数&#xff08;第四届蓝桥杯省赛Java A组/B组 & C B组/C组&#xff09; 4、约数之和&#xff08;《算法竞赛进阶指南》…

图形学 总结 - 老是忘

渲染流水线&#xff1a; 1、首先相机摆放到场景一个位置和角度&#xff0c;场景的各个物体也已经被摆放好 2、拿到场景物体的顶点信息&#xff0c;根据顶点信息构成图元 3、经过透视投影&#xff0c;将图元转化为2*2的正方形&#xff0c;再把2*2的正方形扩展到屏幕大小 4、…

修复 error Delete `␍` prettier/prettier 错误

修复 error Delete ␍ prettier/prettier 错误 问题背景报错信息报错原因解决办法修改CRLF----针对单个文件yarn run lint --fix 一键修复&#xff08;官方提供&#xff09; 问题背景 今天在使用 openapi 自动生成前端接口代码的时候&#xff0c;爆了一个类似 eslint 规范的错…

机器人路径规划:基于流场寻路算法(Flow Field Pathfinding)的机器人路径规划(提供Python代码)

流场寻路算法(Flow Field Pathfinding)是一种基于流体动力学理论的路径规划算法&#xff0c;它模拟了流体在空间中的流动&#xff0c;并利用流体的运动特性来指导路径的选择。下面是流场寻路算法的基本介绍及算法描述&#xff1a; 1. 基本介绍 流场寻路算法通过将环境划分为网…