【C语言】字符函数和字符串函数

目录

一、字符函数

(1)字符分类函数

(2)字符转换函数

二、字符串函数

(1)strlen

① 函数原型

② 注意事项

③ 模拟实现函数

(2)strcpy

① 函数原型

② 注意事项

③ 模拟实现函数

(3)strcat

① 函数原型

② 注意事项

③ 模拟实现函数

(4)strcmp

① 函数原型

② 模拟实现函数

(5)strncpy

① 函数原型

② 注意事项

③ 模拟实现函数

(6)strncat

① 函数原型

② 注意事项

③ 模拟实现函数

(7)strncmp

① 函数原型

② 注意事项

(8)strstr

① 函数原型

② 注意事项

③ 模拟实现函数

(9)strtok

① 函数原型

② 使用示范

(10)strerror

① 函数原型

② 使用示范

(11)perror


一、字符函数

        这些函数的都包含在头文件<ctype.h>里,参考:<cctype> (ctype.h) - C++ Reference (cplusplus.com)

(1)字符分类函数

        字符有数字字符、字母字符等,有时候需要区分字符的类别,就可以用这些函数:

6fd91347747a40b999159d7ee57ff2ad.png

        如果参数符合判断的字符类别,就返回非0值,否则返回0。

(2)字符转换函数

        字符转换函数就是将字母字符转大小写的:

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

        如果传入的参数不符合要求,比如传入 '!',那么就不会对参数进行处理。

二、字符串函数

        字符串的函数都包含在<string.h>头文件中,参考:<cstring> (string.h) - C++ Reference (cplusplus.com)

(1)strlen

① 函数原型

        该函数的作用是计算字符串的长度。

size_t strlen ( const char * str );

② 注意事项

  • 从指定位置开始,到 '\0' 结束,统计 '\0' 之前的字符个数。因此,参数必须有 '\0'。
  • 返回值是 size_t 无符号整数(长度没有负数),这是一个易错点。例如,看看下面代码的结果是什么?

448fa8b6d46b4600b91a4b3a27575262.png

        正确结果是打印">=",很容易错误地认为打印'<',原理如下:

        strlen(s1)返回值3,strlen(s2)返回值6,3-6=-3,但左边结果为无符号整数,右边0默认是有符号整数,根据算术转换规则,会将右边的有符号整数0转换为无符号整数。

[-3]有符号整数

= [1000 0000 0000 0000 0000 0000 0000 0011]原

= [1111 1111 1111 1111 1111 1111 1111 1100]反

= [1111 1111 1111 1111 1111 1111 1111 1101]补

= [1111 1111 1111 1111 1111 1111 1111 1101]无符号整数原

= 4294967293

        结果是左边的值大于右边的0,打印">="。

        因此,对于用strlen判断两个字符串的长度,最好这样用:

if( strlen(s1) > strlen(s2) ){
    ;
}

// 或者

if( (int)strlen(s1) - (int)strlen(s2) > 0 ){
    ;
}

③ 模拟实现函数

        方法1:计数器

d55c1006db274146a77ca6d659590361.png

        方法2:指针 - 指针

8a735bfb4f0b4bc1aac5ecfb1f0bb4f0.png

        方法3:递归

9fb756e1a03145a3b68f0cac5fef498f.png

(2)strcpy

① 函数原型

        该函数的作用是将源字符串(source)拷贝到目标字符串(destination)中。

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

② 注意事项

  • 以源字符串中的 '\0' 作为拷贝结束的标志,'\0'也会复制到目标字符串中。因此,源字符串必须含有 '\0'。否则,会越界拷贝,直到遇到 '\0' 结束。
  • 目标字符串的空间必须足够大,至少要不能小于源字符串的空间大小。
  • 目标字符串必须可修改,才能拷贝字符到里面。因此不能用 const 修饰。
  • 返回值是拷贝好后的目标字符串的首字符地址。有返回值的原因,是给用户更好的体验,比如能够实现链式调用:

32210134239c49019d511f034eb157be.png

  • 以下几种写法都是错误的:

6c0993f58eca4361bf975ae57f25095c.png

1e73b2127972454c99b04b327a69f54d.png

③ 模拟实现函数

921f5f4b74bd4e2a82123006c9cbd1f2.png

(3)strcat

① 函数原型

        该函数的作用是将源字符串追加到目标字符串的末尾。

char *my_strcat(char *dest, const char*src);

② 注意事项

  • 从目标字符串的 '\0' 开始(拷贝内容会把目标字符串结尾的 '\0'覆盖掉),将源字符串拷贝到目标字符串中,直到将源字符串中的 '\0' 也拷贝进去。因此,源字符串和目标字符串,都必须有 '\0'。
  • 目标字符串的空间要足够大,能容下拷贝后的内容。
  • 目标字符串的空间必须可修改,不能用const修饰。
  • 不能自己追加自己,原因如下:

5e5813838fa84a608abb21c8b3b3c3c5.png

        根据C标准,strcpy是不能自己追加自己的,但是VS实现的strcpy能自己追加自己。我们以C标准为准,因为不是所有的编译器实现的库函数都能自己追加自己。

③ 模拟实现函数

b9b51a07a51b43c1a6ed79e467e6b75a.png

(4)strcmp

① 函数原型

        它的功能是比较 str1 和 str2 指向的字符串大小,通过比较两个字符串对应位上的字符的ASCII码来实现。

        str1 大于 str2 指向的字符串则返回值大于0;str1 等于 str2 指向的字符串则返回值等于0;str1 小于 str2 指向的字符串则返回值小于0。

        例如比较 "abc" 和 "abcd",前三个字符都相同,第四位字符是将 '\0' 和 'd'进行比较,对应ASCII码是 0 < 100,因此 "abc" 小于 "abcd"。

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

② 模拟实现函数

        接下来要学习strncpy、strncat、strncmp函数,它们会限制操作的字符串长度;而strcpy、strcat、strcmp不同的是,它们不限制操作的字符串长度,直到字符串结尾为止。

(5)strncpy

① 函数原型

        它的作用是,将源字符串的前n个字符拷贝到目标字符串中。

 char * strncpy ( char * destination, const char * source, size_t num );

② 注意事项

  • 如果源字符串长度小于num,且源字符串结尾没有'\0',在拷贝完源字符串后,会越界拷贝,因此源字符串结尾必须有'\0'。

  • 如果源字符串长度小于num,在拷贝完源字符串后,会在目标字符串后追加 ‘\0’ ,直到达到num个。

  • 如果源字符串长度大于num,拷贝完前num个字符后,不会在结尾追加'\0'。

③ 模拟实现函数

(6)strncat

① 函数原型

        它的作用是将源字符串的前num个字符追加到目标字符串末尾,并追加一个'\0'。

char * strncat ( char * destination, const char * source, size_t num );

② 注意事项

  • 若源字符串长度小于num,则只会将源字符串(包括\0)追加到目标字符串中,不会追加其它的字符。

  • 当num小于源字符串长度时,在追加完前num个字符后,会在结尾再追加一个'\0'。

  • 因为能控制追加的字符个数,所以可以解决strcat不能自己追加自己的问题。

③ 模拟实现函数

(7)strncmp

① 函数原型

        它的作用是最多比较str1和str2指向的字符串的前num个字符的大小。

int strncmp ( const char * str1, const char * str2, size_t num );

② 注意事项

        如果num大于本该比较的长度,则按本该比较的长度来。

        这时并不是比较s1和s2的前8个字符,而是比较到 "ab" 结尾的 '\0' 为止。

(8)strstr

① 函数原型

        它的作用是:如果str2是str1的子串,则返回str2在str1中第一次出现的位置;如果str2不是str1的子串,则返回NULL。

char * strstr ( const char * str1, const char * str2);

② 注意事项

        字符串的比较不包含'\0',但以'\0'为结束标志。

        比如返回 “bba” 在 "aabbaa" 中第一次出现的位置,不会比较"bba"结尾的 '\0',但是会因为'\0'而结束比较,此时表示匹配成功;比如返回"nba" 在 "abcd"中第一次出现的位置,因为"abcd"里没有"nba",所以比较完 "abcd" 的 'd' 和 "nba" 的 'n'后,是"abcd"的最后一个字符'\0',到此结束所有比较,匹配失败。

③ 模拟实现函数

        s1和s2分别指向字符串str1和str2当前比较的字符地址。cur用于指向字符串str1的当前比较的子串首字符地址,便于在这个子串不匹配时,立马比较下一个子串,即s1指向cur的下一个字符开始比较。str2用来保存str2的首字符地址,在当前子串匹配失败时,s2立即返回到str2。

        比较s1指向的a和s2指向的b,不匹配,cur更新为自增1,s1指向cur的位置(下面子串结尾的\0画掉了,自行脑补):

        比较s1指向的b和s2指向的b,匹配,s1和s2分别指向下一个字符:

        比较s1指向的b和s2指向的a,不匹配,cur自增1,s1指向cur的位置,s2回到str2:

        比较s1指向的b和s2指向的b,匹配,s1和s2分别指向下一个字符:

        比较s1指向的a和s2指向的a,匹配,s1和s2分别指向下一个字符:

        比较s1指向的a和s2指向的a,匹配,s1和s2分别指向下一个字符:

        子串的s2指向的 '\0',匹配成功,返回指针cur。

        如果子串str2不在str1里面,s2一直不能指向'\0',那么cur就会一直加加,直到cur指向'\0',则返回NULL。

(9)strtok

① 函数原型

char * strtok ( char * str, const char * sep);
  • sep指向一个字符串,里面是一些分隔符。
  • str指向一个字符串,里面是一些被sep中的0到多个分隔符分割的0到多个标记。
  • strtok找到str中的下一个标记后,会将其结尾的分隔符替换成'\0',并返回这个标记的首字符地址。(因为strtok会改变str的内容,我们又不希望改变原字符串的内容,所以传给str的通常是原字符串的拷贝内容,并且这个拷贝的内容是允许被修改的。)

  • 如果strtok函数的str参数不为NULL,将找到str中的第一个标记,并把标记的位置保存下来。
  • 如果strtok函数的str参数为NULL,将找到同一个字符串中保存的位置的下一个标记,并把标记的位置保存下来。
  • 当字符串中没有更多标记时,strtok函数返回NULL。

② 使用示范

        这个函数看着挺奇怪,str只需要第一次传给它,后面传NULL就能根据保存的位置找到下一个标记。但是在之前学过,函数在调用结束后会将局部变量的空间销毁,那这个位置是怎么保存的?每次更改后的str又是怎么保留下来的?我们可以大胆猜测,里面肯定有static修饰的变量,这样就能把这些变量的生命周期改为跟运行的程序的生命周期一样长了。

(10)strerror

① 函数原型

        它的作用是返回错误码errnum对应的错误信息的字符串地址。

 char* strerror ( int errnum );

        在一些系统和C语言标准库中都定义了一些错误码,这些错误码一般在<errno.h>头文件中说明。程序运行时会定义一个全局变量 errno,来记录当前的错误码。程序一开始运行时,errno初始化为0,表示没有错误;当调用标准库中的函数发生错误时,就会将errno赋值为错误对应的错误码。因为错误码只是一个整数,我们无法理解它代表的意思,所以strerror函数可以返回错误码对应的错误信息字符串的地址,帮助我们知道当前发生了什么错误。

② 使用示范

(11)perror

        perror == print + error,功能就是直接打印errno对应的错误信息。输出到屏幕的结果是:参数中的字符串 + 一个冒号 + 一个空格 + 错误信息。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值