【落羽的落羽 C语言篇】一些常见的字符函数、字符串函数、内存函数

在这里插入图片描述

文章目录

  • 一、字符函数
    • 1. 字符分类函数
    • 2. 字符转换函数
  • 二、字符串函数
    • 1. strlen的使用和模拟实现
      • 使用
      • 模拟实现
    • 2. strcpy的使用和模拟实现
      • 使用
      • 模拟实现
    • 3. strcat的使用和模拟实现
      • 使用
      • 模拟实现
    • 4. strcmp的使用和模拟实现
      • 使用
      • 模拟实现
    • 5. strncpy的使用
    • 6. strncat的使用
    • 7. strncmp的使用
    • 8. strstr的使用和模拟实现
      • 使用
      • 模拟实现
    • 9. strchr的使用
    • 10. strrchr的使用
    • 11. strtok的使用
    • 12. strerror的使用
  • 三、内存函数
    • 1. memcpy的使用和模拟实现
      • 使用
      • 模拟实现
    • 2. memmove的使用和模拟实现
      • 使用
      • 模拟实现
    • 3. memset的使用
    • 4. memcmp的使用

一、字符函数

1. 字符分类函数

C语言中有一些库函数是专门用来分类字符的,这些函数的使用都需要包含头文件ctype.h

函数检测类型
iscntrl任何控制字符
isspace空白字符:包括空格’ ‘,回车’\r’,换行’\n’等
isdigit十进制数字:0到9
isxdigit十六进制数字:0到9,a到f,A到F
islower小写字母:a到z
isupper大写字母:A到Z
isalpha字母:a到z,A到Z
isalnum字符或数字:a到z,A到Z,0到9
ispunct标点符号:不属于字符和数字的可打印图形字符
isgraph所有图形字符
isprint所有可打印字符,包括图形字符和空白字符

注意:这些函数的传参实际上都是字符的ASCII值,参数是int类型
这些函数的使用方法几乎相同,我们就以isdigit为例讲解,它的定义是:

int isdigit(int c)

在使用时,如果isdigit的参数是一个十进制数,那么函数的返回值就是非0的整数;如果不是十进制数,返回值就是0。
上面其他的函数都是这个道理,如果参数是是它们对应的检测类型,函数的返回值是非0的整数(真),反之则是0(假)。

举个栗子吧,利用函数islower,将一个字符串中的小写字母转换成大写字母:

#include<stdio.h>
#include<ctype.h>
int main()
{
char s[10] = {'\0'};
gets(s);
for(int i=0 ; s[i]!='\0' ; i++){if(islower(s[i]))s[i]-=32;}
puts(s);
return 0;
}

在这里插入图片描述
非常好理解!

2. 字符转换函数

在ctype.h头文件中,还有两个函数tolower、toupper,用于转换字符。
函数tolower能把参数的大写字母转换成小写字母并返回,函数toupper能把参数的小写字母转换成大写字母并返回。(它们的参数和返回值都是ASCII的形式,也就是int类型)
如果toupper的参数不是小写字母,tolower的参数不是大写字母,那么返回值还是原来的字符(的ASCII值)

还是上面的例子,将一个字符串中的小写字母转换成大写字母。刚才我们先用islower判断小写字母再减32。但这时,我们就可以直接用toupper达到这个效果:

#include<stdio.h>
#include<ctype.h>
int main()
{
char s[10] = {'\0'};
gets(s);
for(int i=0 ; s[i]!='\0' ; i++)s[i] = toupper(s[i]);
puts(s);
return 0;
}

结果也是正确的
在这里插入图片描述

二、字符串函数

我们在编程或做题时经常与字符串打交道,因此掌握一些字符串相关的库函数是必不可少的。以下这些函数,都包含在头文件string.h中。对于一些重要的函数,很多企业在笔试时都会考察它们的模拟实现(关于模拟实现是什么,请见《指针·之其五》),所以我们也应该掌握。

1. strlen的使用和模拟实现

使用

在这里插入图片描述strlen的返回值是字符串str的长度(字符个数),它从指针str开始计数,以\0为字符串的结束标志。注意:strlen的返回类型size_t是无符号的,如果有表达式strlen(s1) - strlen(s2),不论s1和s2谁长谁短,结果都是无符号数(非负数)

模拟实现

  • 思路1:最简单的遍历计数
int Mine_strlen(const char* str)
{int count=0;assert(str);//防止str是空指针while(*str!='\0'){count++;str++;}return count;
}
  • 思路2:利用递归的思想计数
int Mine_strlen(const char* str)
{assert(str);if(*str=='\0')return 0;elsereturn 1+Mine_strlen(str+1);
}
  • 思路3:利用“指针 - 指针 = 两者间元素个数”的原理计数
int Mine_strlen(const char* str)
{assert(str);char* str1 = str;while(*str1!='\0')str1++;return str1-str;
}

2. strcpy的使用和模拟实现

使用

在这里插入图片描述

strcpy的功能是“拷贝字符串”,它能将source指向的字符串(包括其结尾的\0)拷贝到destination指向的内容。也就是说,这个函数修改的是destination指向的字符串的内容。它的使用要点是源字符串必须以\0结尾,目标空间必须可修改,而且要有足够大以确保能容纳的下源字符串。
举个栗子:

#include<stdio.h>
#include<string.h>
int main()
{char s1[] = "xxxxxxx";char s2[] = "abc";strcpy(s1,s2);printf("%s\n",s1);//这两句也可以直接写成printf("%s\n",strcpy(s1,s2));(链式访问)return 0;
}

s2中的“a”、“b”、“c”、“\0”都会被拷贝到s1中,替换掉s1的前四个字符,s1的内容变成了“abc\0xxx\0”,而s2的内容是不变的。%s打印s1,结果是abc
在这里插入图片描述

模拟实现

思路:遍历source字符串,一个一个赋值

char* Mine_strcpy(char* destination,const char* source)
{assert(destination&&source);char* ret = destination;while(*(destination++) = *(source++));return ret;
}

3. strcat的使用和模拟实现

使用

在这里插入图片描述strcat的功能是“连接字符串”,它会将source字符串连接到destination字符串之后,本质上是用源字符串的内容替换目标字符串结尾的\0。也就是说,这个函数修改的是destination字符串的内容。它的使用要点是:目标空间中必须有\0,目标空间必须足够大能容纳的下源字符串的内容,目标空间必须可修改。
举个栗子:

#include<stdio.h>
#include<string.h>
int main()
{char s1[20] = "Genshin";char s2[] = " Impact";strcat(s1,s2);printf("%s\n",s1);return 0;
}

在这里插入图片描述

模拟实现

思路:先找到目标字符串的结尾,再一个一个赋值

char* Mine_strcat(char* destination,const char* source)
{assert(destination&&source);char* ret = destination;while(*destination!='\0')destination++;while(*(destination++) = *(source++));return ret;
}

4. strcmp的使用和模拟实现

使用

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

strcmp能比较两个字符串:str1大于str2,则返回大于0的数字;str1等于str2,则返回0;str1小于str2,则返回小于0的数字。
那么如何比较两个字符串的大小呢?不是根据字符串的长度,而是依次比较相应位置上字符的ASCII值。例如,str1是abcdef,str2是abq,比较第一个字符,ASCII值相同;比较第二个字符,ASCII值相同;比较第三个字符,str2的q比str1的c的ASCII值大。所以str2就比str1大。

模拟实现

int Mine_strcmp(const char* str1,const char* str2)
{assert(str1&&str2);while(*str1==*str2){if(*str1=='\0')return 0;str1++;str2++;}return *str1-*str2;
}

在这里插入图片描述

strcpy、strcat、strcmp、strncpy、strncat、strncmp在VS中使用时,可能会不安全而报错,在函数名之后加上_s就行。
刚才我们学习的strcpy、strcat、strcmp函数,都是针对长度不受限制的字符串使用的,可能不太安全。所以有时候,我们不会操作整个字符串,而要限制字符数。对此就要使用以下三个函数strncpy、strncat、strncmp。

5. strncpy的使用

在这里插入图片描述

strncpy也是用来拷贝字符串的,它的使用要求几乎和strcpy相同。但它不像strcpy只能将source字符串整个拷贝过去,它可以规定拷贝的字符个数,也就是参数部分的size_t num。当然,如果num超过了str2本身的长度,str2的全部内容都会拷贝过去。
举个栗子:

#include<string.h>
#include<stdio.h>
int main()
{char str1[] = "xxxxxxx";char str2[] = "abcdef";strncpy(str1,str2,3);printf("%s\n",str1);return 0;
}

在这里插入图片描述

6. strncat的使用

在这里插入图片描述

这也很好理解了,strncat的使用和strcat几乎相同,都是连接字符串的,但strncat还可以规定把str2的几个字符连接过去。当然,如果num超过了str2本身的长度,str2的全部内容都会连接过去。
举个栗子:

#include<string.h>
#include<stdio.h>
int main()
{char str1[20] = "honkai ";char str2[20] = "railstarxxxxxxx";strncat(str1,str2,8);printf("%s\n",str1);return 0;
}

在这里插入图片描述

7. strncmp的使用

在这里插入图片描述

同样地,相较于strcmp,strncmp可以规定比较前几个字符。从第一个开始比较,如果相同就继续往后比较,最多比较num个字符。如果都想等,就返回0。
在这里插入图片描述

8. strstr的使用和模拟实现

使用

在这里插入图片描述

strstr能够找到str2在str1中第一次出现的位置,字符串的比较匹配不包含\0字符,以\0字符作为结束标志。如果能够在str1中找到str2,就返回str2第一次出现的位置(地址);如果找不到,就返回空指针NULL。
举个栗子:

#include<string.h>
#include<stdio.h>
int main()
{char str1[] = "honkai railstar";char str2[] = "kai";char* p = strstr(str1,str2);printf("%d\n", p-str1);return 0;
}

在这里插入图片描述

模拟实现

char* Mine_strstr(char* str1, const char* str2)
{assert(str1&&str2);const char* cur = str1;//开始匹配的位置const char* s1 = NULL;//遍历str1const char* s2 = NULL;//遍历str2while(*cur!='\0'){s1 = cur;s2 = str2;while((*s1!='\0')&&(*s2!='\0')&&(*s1==*s2))//依次比较str2和str1中的字符{s1++;s2++;}if(*s2=='\0')//如果str2的每个字符都匹配成功,就是在str1中找到了str2return cur;cur++;}return NULL;
}

9. strchr的使用

在这里插入图片描述

strchr很简单,它能找到一个字符在一个字符串中第一次出现的位置并返回,如果找不到就返回空指针NULL。
举个栗子:

#include<string.h>
#include<stdio.h>
int main()
{char str1[] = "honkai railstar";char* p = strchr(str1,'k');printf("%d\n", p-str1);return 0;
}

在这里插入图片描述

10. strrchr的使用

在这里插入图片描述
strrchr也是查找一个字符串中的特定字符,但它返回的是这个字符在字符串中最后一次出现的位置,如果找不到就返回空指针NULL。值得注意的是,指定字符的参数是int类型。
举个例子:

#include<string.h>
#include<stdio.h>
int main()
{char str1[] = "abcda";char* p = strrchr(str1,'a');printf("%d\n", p-str1);return 0;
}

在这里插入图片描述

11. strtok的使用

strtok函数的使用较为特殊
在这里插入图片描述

简单来说,strtok的作用是分割字符串,它的使用形式是char* strtok(char* str, char* sep);。其中str是待分割的字符串,而sep是当做分隔符的字符的数组。在使用时,函数会从左向右读取字符串str,遇到第一个分隔符时,会把它变成\0,然后返回str的起始位置。所以这个函数会改变str的内容,在实际使用时我们常常将原字符串复制一份再分割。
比如我们假设:

char str[] = "abcxdef.ghi@jkl";
char sep[] = "@x.";
char* p = strtok(str,sep);

此时%s打印p,结果是abc,因为str的x变成了\0。

但倘若我们再用一次strtok,但第一个参数设成NULL,则函数会从上一次分隔符的出现的地方开始读取,再寻找分隔符,也就是从d开始读取,直到找到了“.”,设置成\0返回d的地址。而如果函数往后读取到结束都找不到分隔符了,就返回空指针NULL。

在这里插入图片描述

12. strerror的使用

在这里插入图片描述

在不同的系统和C语言标准库中,都规定了一些错误码,一般放在了errno.h这个头文件说明,C语言程序启动时会使用一个全局的变量errno来记录程序当前的错误码。程序启动时errno是0,表示没有错误。每一个错误码都对应一种运行错误,当我们在使用标准库中的函数时发生了某种错误,就会将对应的错误码数储存在中errno中。strerrno的参数部分就是错误码,他可以将对应的错误信息字符串地址返回。
比如我们在Windows11的VS2022环境下看一看1到10对应的错误信息都是什么?

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{for(int i=1 ; i<=10 ; i++)printf("%s\n",strerror(i));return 0;
}

在这里插入图片描述

除此之外,在stdio.h头文件中还有一个函数perror,也可以了解一下
在这里插入图片描述perror函数相当于结合了printf和strerror函数,它先打印出它的参数指向的字符串,再打印一个“:”,再打印出变量errno储存的错误码对应的错误信息字符串。
例如:

#include<stdio.h>
#include<errno.h>
int main()
{//这个程序没有错误,errno是0,对应的错误信息是“No error”perror("Is there an error");return 0;
}

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

三、内存函数

C语言的指针是其精髓之一,而指针和内存千丝万缕。所以我们也应该掌握一些与内存相关的函数。以下函数也包含在string.h头文件中。

1. memcpy的使用和模拟实现

使用

在这里插入图片描述
函数memcpy会从source的位置开始向后拷贝num个字节的数据到destination指向的内存位置,这个函数在遇到\0前不会停止拷贝。如果source和destination有内存重叠的部分,那么函数是无法保证运行结果的,比如

int a[5]={1,2,3,4,5};
memcpy(a+2, a, 3*sizeof(int));

我们不知道3是先拷贝到a[4]的位置再变成了1,或是3先变成了1再拷贝到a[4]的位置。运行后a中可能变成1,2,1,2,3,也可能变成1,2,1,2,1。所以我们就不要用memcpy处理内存有重叠的两块区域了,而可以用memmove来实现。

模拟实现

思路:一个字节一个字节地拷贝

void* Mine_memcpy(void* destination, const void* source, size_t num)
{assert(destination&&source);void* ret = destination;while(num--){*(char*)destination = *(char*)source;destination=(char*)destination+1;source=(char*)source+1;}return ret;
}

2. memmove的使用和模拟实现

使用

在这里插入图片描述memmove和memcpy的作用完全相同,差别就是memmove函数处理的源空间和目标空间可以重叠。使用memmove就可以将被拷贝数据完整地拷贝过去,不会出现某些数据被拷贝前就被修改的情况了。

int a[5]={1,2,3,4,5};
memmove(a+2, a, 3*sizeof(int));

这样,a中一定是1,2,1,2,3

int a[5]={1,2,3,4,5};
memmove(a, a+2, 3*sizeof(int));

这样,a中一定是3,4,5,4,5

模拟实现

思路:为了防止被拷贝数据被覆盖,分成两种情况,从后向前依次拷贝,或是从前向后依次复制

void* Mine_memmove(void* destination, const void* source, size_t num)
{assert(destination&&source);void* ret = destination;if(destination<source){while(num--){*(char*)destination = *(char*)source;destination = (char*)destination+1;source = (char*)source+1;}}else{while(num--)*((char*)destination+num) = *((char*)source+num);}return ret;
}

3. memset的使用

在这里插入图片描述
memset是用来设置内存的,可以将内存中的值以字节为单位设置成想要的内容。它可以把ptr指向的内存的前num个字节设置成给定的value值。
举个栗子:

#include<stdio.h>
#include<string.h>
int main()
{char str[] = "abcde";memset(str, 'x', 3);printf("%s\n",str);return 0;
}

在这里插入图片描述

4. memcmp的使用

在这里插入图片描述
memcmp能比较ptr1和ptr2指针指向的位置开始向后的num个字节,返回值的规则和strcmp类似
在这里插入图片描述

这里就不再举例子赘述了(我也累了~)

在这里插入图片描述

本篇完,感谢阅读

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

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

相关文章

JAVA:访问者模式(Visitor Pattern)的技术指南

1、简述 访问者模式(Visitor Pattern)是一种行为型设计模式,允许你将操作分离到不同的对象中,而无需修改对象本身的结构。这种模式特别适合复杂对象结构中对其元素进行操作的场景。 本文将介绍访问者模式的核心概念、优缺点,并通过详细代码示例展示如何在实际应用中实现…

小米自研系统Vela全面开源:开启物联网新时代的技术革新之旅

目录 Vela系统的技术特点 1. 高性能与低功耗的完美平衡 2. 高度可扩展性与模块化设计 3. 强大的安全机制 4. 跨平台兼容性 Vela系统的应用场景 1. 智能家居领域 2. 工业物联网领域 3. 医疗健康领域 4. 智慧城市领域 Vela系统的深远影响 1. 推动物联…

Linux/CentOS编译TensorFlow

很多时候为了方便图省事&#xff0c;是通过pip安装TensorFlow的&#xff0c;然而很不幸运行的服务器不支持AVX指令&#xff0c;引入模块的时候会报错&#xff1a; The TensorFlow library was compiled to use AVX instructions, but these aren’t available on your machine.…

2021陇剑杯——流量分析

JWT简介 JWT&#xff08;JSON Web Token&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用环境中以一种紧凑的、URL安全的方式传递声明&#xff08;Claims&#xff09;。JWT通常用于身份验证、信息交换以及验证消息的完整性。JWT通过在不…

visual studio 2022 c++使用教程

介绍 c开发windows一般都是visual studio&#xff0c;linux一般是vscode&#xff0c;但vscode调试c不方便&#xff0c;所以很多情况都是2套代码&#xff0c;在windows上用vs开发方便&#xff0c;在转到linux。 安装 1、官网下载vs2022企业版–选择桌面开发–安装位置–安装–…

Grafana配置告警规则推送企微机器人服务器资源告警

前提 已经部署Grafana&#xff0c;并且dashboard接入数据 大屏编号地址&#xff1a;Node Exporter Full | Grafana Labs 创建企微机器人 备注&#xff1a;群里若有第三方外部人员不能创建 机器人创建完成&#xff0c;记录下来Webhook地址 Grafana配置告警消息模板 {{ define &…

yolov7-搭建及测试

1.环境配置 参考链接&#xff0c;建立环境 2.YOLOv7代码下载 代码及论文地址&#xff1a; GitHub - WongKinYiu/yolov7: Implementation of paper - YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors 下载zip后解压 注意&am…

HCIA-Access V2.5_2_2_2网络通信基础_IP编址与路由

网络层数据封装 首先IP地址封装在网络层&#xff0c;它用于标识一台网络设备&#xff0c;其中IP地址分为两个部分&#xff0c;网络地址和主机地址&#xff0c;通过我们采用点分十进制的形式进行表示。 IP地址分类 对IP地址而言&#xff0c;它细分为五类&#xff0c;A,B,C,D,E,…

信号处理相关的东东(学习解惑)

信号处理相关的东东&#xff08;学习解惑&#xff09; 所有内容学习自知乎专栏&#xff0c;https://www.zhihu.com/column/xinhao&#xff0c;写的很好&#xff0c;值得反复学习 时频域分析的一些常用概念 FROM&#xff1a;https://zhuanlan.zhihu.com/p/35742606 1、相加性…

【cocos creator】按照行列数创建格子布局

调用 this.creatLayout(5, 5, this.boxNode, this.rootNode) //限制数量 this.creatLayout(5, 5, this.boxNode, this.rootNode, cc.v3(0, 0), 10, 10, 23) /*** 创建格子布局* param xCount 列数量* param yCount 行数量* param prefab 预制体* param root 根节点* param root…

WPF 控件

<div id"content_views" class"htmledit_views"><p id"main-toc"><strong>目录</strong></p> WPF基础控件 按钮控件&#xff1a; Button:按钮 RepeatButton:长按按钮 RadioButton:单选按钮 数据显示控件 Te…

java中List集合小练习

题目&#xff1a;将1~100之间所有正整数存放在一个List集合中&#xff0c;并将集合索引位置时10的对象从集合中移除。 代码&#xff1a; import java.util.ArrayList; import java.util.List;public class ListTest {public ListTest(){List<Integer> listnew ArrayLis…

RK3566触摸驱动产品实战 配置设备树 I2C驱动框架(附源码)

引言&#xff1a;相信大家在学完相关驱动框架很少真正的运用在实际的产品案例中&#xff0c;对设备树以及驱动框架还是非常的陌生&#xff0c;其次就是在编写相关驱动还有完成项目任务时的一些思路的引导&#xff0c;这些都是需要补足的&#xff0c;接下来&#xff0c;我们将之…

Qt WORD/PDF(二)使用 QtPdfium库实现 PDF操作、打印等

关于QT Widget 其它文章请点击这里: QT Widget GitHub 源码: QWidgetLearningPro &#xff08;暂未更新&#xff09; 姊妹篇: Qt WORD/PDF&#xff08;一&#xff09;使用 QtPdfium库实现 PDF 预览 一、简介 QtPdfium 是基于Pdfium库的一个Qt绑定。Pdfium是一个…

信息收集(dns信息收集dnsenum和路由收集traceroute)

导入 被动收集方式不容易被目标域发现 DNS信息收集 网络拓扑结构以及网络设置 服务机 路由器 kali机 查看并设置默认网关 工具 或者在系统目录下可找到dns的工具 dnsenum dnsenum -h 帮助指令 收集百度域的指令 使用字典文件破解对查找目标域进行收集 dnsmap 对自己的虚拟机…

Java设计模式 —— 【结构型模式】适配器模式(类的适配器、对象适配器、接口适配器)详解

文章目录 基本介绍一、类的适配器二、对象适配器三、接口适配器总结 基本介绍 生活中有很多例子&#xff1a; 不同国家的插座接口不同&#xff0c;需要转换器&#xff1b;家用电源220V&#xff0c;手机只接受5V充电&#xff0c;需要转换器&#xff1b;读卡器&#xff0c;拓展…

启明智显ZX7981PC:5G时代的新选择,全屋网络无缝覆盖

在这个飞速发展的5G时代&#xff0c;每一个细微的科技进步都在推动着我们的生活向更加智能、便捷的方向发展。近日&#xff0c;启明智显再次引领科技潮流&#xff0c;正式发布其最新的5G CPE产品——ZX7981PC。作为继7981PG与7981PM之后的又一次迭代升级&#xff0c;ZX7981PC凭…

11篇--图像边缘检测

图像梯度 要学习图像边缘检测&#xff0c;要先了解图像梯度的概念&#xff0c;我们正是通过梯度值来区分边缘像素点的 处于边缘附近的像素点与周围像素点的差距很大&#xff08;不然不会有边缘呈现&#xff09;&#xff0c;所以给边缘附近的的梯度之变化很快&#xff0c;通过…

6.2 MapReduce工作原理

MapReduce工作原理涉及将大数据集分割成小块并行处理。Map任务读取数据块并输出中间键值对&#xff0c;而Reduce任务则处理这些排序后的数据以生成最终结果。MapTask工作包括读取数据、应用Map函数、收集输出、内存溢出时写入磁盘以及可选的Combiner局部聚合。ReduceTask工作则…

ARM Cortex-A7 MPCore 架构

1、Cortex-A7 MPCore 简介 Cortex-A7 MPcore 处理器支持 1~4 核&#xff0c;通常是和 Cortex-A15 组成 big.LITTLE 架构的&#xff0c; Cortex-A15 作为大核负责高性能运算&#xff0c;比如玩游戏啥的&#xff0c; Cortex-A7 负责普通应用&#xff0c;因为 CortexA7 省电。 Co…