字符串是c语言中一种常见的数据类型,字符串属于常量,不可修改,如果我们想要修改,就要把它放在字符数组中来对其进行修改,而有一些函数可以帮助我们完成一些常用的操作字符串的动作,下面我们就来介绍一些常用字符串操作函数

文章目录

  • *strlen*
  • *strcpy*
  • *strcat*
  • *strcmp*
  • *strncpy*
  • *strncat*
  • *strncmp*
  • *strstr*

strlen

strlen

strlen的作用是计算字符串的长度,但不包括最后的\0
所以我们在传参的时候要注意是不是以\0结尾的字符串
我们先来看函数原型

size_t strlen ( const char * str );

形参是用一个字符指针接收字符串的首字符地址

返回类型是个无符号的整型,因为字符串的长度肯定是个非零的数,但这里有一个地方需要注意,看下面:

#include <stdio.h>int main(){const char*str1 = "abcdef";const char*str2 = "bbb";if(strlen(str2)-strlen(str1)>0)//strlen返回的是一个无符号的整型,相减的结果数值上等于-3//但转化为无符号整型是一个无穷大的数 {printf("str2>str1\n");} else{printf("srt1>str2\n");}return 0;
}

所以打印结果是str2>str1

strlen的使用情况并不是很复杂,接下来模拟实现strlen:(解释见代码注释)

方法1:

计数器实现

int my_strlen1(const char* str)//计数器实现strlen
{assert(str);//判断str是否为空指针int count = 0;while (*str++)//str先进行后置++,再进行解引用操作//如果*str != \0,进入循环计数{count++;//进入循环一次使count自增一次}return count;//当循环结束时,count此时就是字符串的长度
}int main()
{char arr[] = "hello world";printf("%d\n", my_strlen1(arr));return 0;
}

方法2:

递归实现

int my_strlen2(const char* str)
{if (*str)//如果*str != \0,进入递归return 1 + my_strlen2(str + 1);//返回1+剩下的字符串长度else//当*str = \0,返回0,此时就可计算出字符串长度return 0;
}int main()
{char arr[] = "hello world";printf("%d\n", my_strlen2(arr));return 0;
}

方法3:

指针减指针实现

int my_strlen3(const char* str)//指针-指针实现strlen
{assert(str);char* start = str;//记录首字符地址while (*str)//*str != \0时,str指针跳向下一个字符的地址{str++;}return str - start;//循环结束时,str指向最后一个字符(不是\0),//str - start即为两者之间元素的个数,也就是字符串长度
}
int main()
{char arr[] = "hello world";printf("%d\n", my_strlen3(arr));return 0;
}

我们也可以看看vs2019提供的strlen:

size_t __cdecl strlen (const char * str)
{const char *eos = str;while( *eos++ ) ;return( eos - str - 1 );
}

求字符串长度时i,字符串不可写成arr[] = {‘a’, ‘b’, ‘c’}的形式,否则计算的是随机值,因为该数组末尾是没有\0的

strcpy

strcpy

我们对数组赋值时不能写成 arr = “abcd”,arr表示的是数组名,这种赋值方式是不合法的,这时就可以使用strcpy

strcpy的作用是将一个字符串的内容拷贝到另一个字符串

strcpy的原型:

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

参数是两个字符串,将第二个参数的内容拷贝至第一个参数

返回类型是第一个字符串的首字符地址

使用strcpy应注意的点:

1.源字符串必须以 ‘\0’ 结束。

2.会将源字符串中的 ‘\0’ 拷贝到目标空间。

3.目标空间必须足够大,以确保能存放源字符串。否则会导致数组越界访问

4.目标空间必须可变。必须要是可修改的数组

我们同样来实现strcpy:

char* my_strcpy(char* dst, const char* src)
{assert(dst && src);char* start = dst;//记录首字符地址,以便返回while (*dst++ = *src++)// \0也会被拷贝到arr1中{;}return start;
}int main()
{char arr1[20] = "hello world";char arr2[] = "didididi";printf("%s\n", strcpy(arr1, arr2));
}

strcat

strcat

strcat是将一个字符串的内容连接在另一个字符串的后面

函数原型:

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

将第二个参数的内容连接在第一个参数之后

返回第一个字符串首字符地址

使用strcat需要注意的地方:

1.源字符串必须以 ‘\0’ 结束。’\0’也会被赋给目标字符串

2.目标空间必须有足够的大,能容纳下源字符串的内容。

3.目标空间必须可修改。

字符串自己给自己连接可以吗?

不可行,我们先模拟实现strcat,知道原理后再解释

char* my_strcat(char* dst, const char* src)//模拟实现strcat
{assert(dst && src);char* start = dst;while (*dst)//找到目标字符串的末尾{dst++;}while (*dst++ = *src++) //当把源字符串中的'\0'赋给目标字符串后,返回目标字符串的首元素地址{;}return start;
}int main()
{char arr1[20] = "hello ";char arr2[] = "world";printf("%s\n", my_strcat(arr1, arr2));return 0;
}

了解strcat的原理后,我们就知道,源字符串必须要有’\0’连接才会结束

而如果自己连接自己,会导致’\0’被覆盖掉,因此就没有了结束标志,此时代码就陷入死循环的连接。

strcmp

strcmp

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

标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字

strcmp是对两个字符串进行比较,比较的不是字符串的长度,而是字符中字母的大小(a<b<…<z,大写也一样)

比如abcd大于abbc

如果对应字符相等,则向后继续判断

模拟实现strcmp

int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2)当两个字符串对应元素相等时,向后继续比较{if (*str1 == '\0')//如果两元素都等于'\0'了,说明两字符串相等,返回0return 0;str1++;str2++;}return *str1 - *str2;//当两字符串不相等时,返回对应ASCII码差值
}int main()
{char arr1[30] = "zbc";char arr2[] = "abb";printf("%d\n",  my_strcmp(arr1, arr2));return 0;
}

vs2019的参考实现代码:

int __cdecl strcmp (const char * src,const char * dst)
{int ret = 0 ;while((ret = *(unsigned char *)src - *(unsigned char *)dst) == 0 && *dst)//当两字符串对应元素相等并不为'\0'时,往后继续比较//而当两字符串对应元素相等但已经等于'\0'时,跳出循环,此时ret=0e{++src, ++dst;}return ((-ret) < 0) - (ret < 0); // (if positive) //- (if negative) generates branchless code//如果对应str1(第一个参数)对应字符大于str2(第二个参数)对应字符,ret大于0,此时(-ret)<0为真,ret<0为假//两个表达式相减得1,故此时返回值为1//如果对应str1(第一个参数)对应字符小于str2(第二个参数)对应字符,ret小于0,此时(-ret)<0为假,ret<0为真//两个表达式相减得-1,故此时返回值为-1
}

strncpy

strncpy

函数原型:

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

strncpy的作用是拷贝num个字符从源字符串到目标空间。

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标空间的后边追加\0,直到第num个字符。

我们可以来验证一下:

int main()
{char arr1[30] = "hello world";char arr2[] = "hey";strncpy(arr1, arr2, 5);//5大于arr2的长度return 0;
}

可以看到在把hey拷贝给arr1后,还拷贝了两个\0,即5-3个\0

这里就不实现strncpy了,大家有兴趣可以自行实现

strncat

strncat

函数原型:

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

与strncmp类型,在目标字符串后追加源字符串中num个字符

但当num大于源字符串长度时,只会在源字符串后加1个\0,这一点与strncmp不同

我们同样可以证明:

int main()
{char arr1[30] = "hello\0 world";char arr2[] = "aaa";strncat(arr1, arr2, 7);//7大于arr2的长度return 0;
}

因为连接字符串是从目标字符串的\0开始的,所以在初始化arr1时就加上了\0,方便我们观察连接后arr1的变化

可以看到只追加了一个\0,之后的’l’ 'd’仍然存在

看一个使用strncat的例子:

#include <stdio.h>#include <string.h>int main (){char str1[20];char str2[20];strcpy (str1,"To be ");//str1中被拷贝了To be strcpy (str2,"or not to be");//str2中被拷贝了or not to bestrncat (str1, str2, 6);//将str2中的6个字符连接到str1后puts (str1);return 0;}

打印结果为:

​ To be or not

strncmp

strncmp

函数原型:

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

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

使用:

#include <stdio.h>#include <string.h>int main (){char str[][5] = { "R2D2" , "C3PO" , "R2A6" };int n;puts ("Looking for R2 astromech droids...");//寻找R2开头的字符串for (n=0 ; n<3 ; n++)if (strncmp (str[n],"R2xx",2) == 0)//当返回0时,说明找到了R2开头的字符串{printf ("found %s\n",str[n]);}return 0;
}

str中有两个R2开头的字符串,所以打印结果即为这两个字符串

结果为:

strstr

strstr

strstr是判断一个字符串是否是另一个字符串的子串,即是否包含了另一个字符串

函数原型:

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

​ 判断str2是否为str1的子串

如果是,则返回str1中对应str2字符串的首字符地址

如果不是,则返回空指针

使用例子:

#include <stdio.h>#include <string.h>int main (){char str[] ="This is a simple string";char * pch;pch = strstr (str,"simple");//判断simple是否为This is a simple string的子串//pch即为's'(第三个s)的地址strncpy (pch,"sample",6);//将sample从pch处开始拷贝puts (str);return 0;}

pch即为’s’(第三个s)的地址

打印结果是This is a sample string

模拟实现strstr:

char* mystrstr(const char* str1, const char* str2)
{assert(str1 && str2);if (*str2 == 0)return (char*)str1;const char* sign = str1;//记录第一个相等字符的位置const char* s1 = str1;//用来移动并查找是否与str2相等,因为没找到要跳回sign位置,所以用另外一个指针进行移动const char* s2 = str2;用来移动并查找是否与str1相等,因为没找到要跳回str2位置,所以用另外一个指针进行移动for (; *s1; s1 = sign + 1, s2 = str2)//可以省略初始化条件,判断条件为s1不指向\0//调整部分为每次将s1重新返回,s2也返回                                         {sign = s1;//标记位置while ((*s1) && (*s1 == *s2))//当s1不指向'\0'且*s1 == *s2相等时,继续判断是否相等{s1++;s2++;if (*s2 == '\0')//当s2指向'\0'时,说明str2是str1的子串,返回一个相等字符的位置return (char*)sign;}}return NULL;//当循环结束时,说明没找到该子串,返回NULL}int main()
{char arr1[30] = "abbbcdef";char arr2[] = "";printf("%s\n", mystrstr(arr1, arr2));return 0;
}

或者:

char* mystrstr(const char* str1, const char* str2)
{assert(str1 && str2);if (*str2 == 0)return (char*)str1;const char* sign = str1;const char* s1 = str1;const char* s2 = str2;while (*sign)//*sign != '\0'说明还可以继续查找,否则就没有找到{s1 = sign;//将s1置于标记处s2 = str2;//将s2至于str2起始位置while (* s1 && *s1 == *s2)//当s1不指向'\0'且*s1 == *s2相等时,继续判断是否相等{s1++;s2++;if (*s2 =='\0')//当s2指向'\0'时,说明str2是str1的子串,返回一个相等字符的位置return (char*)sign;       }   sign++;//继续往下查找}return NULL;//循环结束时锁说明没有找到,返回NULL}

以上就是一些常见的字符串操作函数,有兴趣的小伙伴还可以自己了解以下两个函数:

strtok

strerror

strtok

strtok

char * strtok ( char * str, const char * sep );

sep参数是个字符串,定义了用作分隔符的字符集合 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

strtok函数找到str中的下一个标记,并将其用 \0 结尾,每次调用成功则返回指向被分割出片段的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将通过指针保存它在字符串中的位置。

函数保存的指针在下一次调用中将作为起始位置。

strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。

如果字符串中不存在更多的标记,则返回 NULL 指针。

以下是两个简单的例子

大家可以自行打印试试感受下这个函数的作用

#include <stdio.h>#include <string.h>int main (){char str[] ="- This, a sample string.";char * pch;printf ("Splitting string \"%s\" into tokens:\n",str);pch = strtok (str," ,.-");while (pch != NULL){printf ("%s\n",pch);pch = strtok (NULL, " ,.-");}return 0;
}

int main(){char *p = "1993868303@qq.com";const char* sep = "@.";char arr[30];char *str = NULL;strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep)){printf("%s\n", str);}}

strerror

  char * strerror ( int errnum );

返回错误码,所对应的错误信息。

#include <stdio.h> #include <string.h> #include <errno.h>//必须包含的头文件
int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) printf ("Error opening file unexist.ent: %s\n",strerror(errno)); //errno: Last error number return 0; }

errno是一个全局的错误码,相当于一个全局变量,当出现错误时,错误码会被放入其中,如果想知道错误是什么,可以使用strerror函数,会返回一个指向错误信息字符串的指针,此时打印出来就能知道错误在哪了。

使用errno需要包含头文件

记得一键三连!!!

c语言strcpy、strcmp、strcat等常见字符操作函数的介绍相关推荐

  1. C语言字符串库函数详解模拟实现(strlen、strcpy、strcat、strcmp)+字符操作函数+字符转换函数

    字符串库函数详解 一.无字符串长度限制的字符串函数 1. strlen 计算字符串长度函数 2. strcpy 字符串拷贝函数 3. strcat 字符串追加函数 4. strcmp 字符串比较函数 ...

  2. C语言库函数的实现(strlen strcpy strcmp strcat strstr)

    库函数的实现 strlen strcpy strcmp strcat strstr assert函数 头文件:assert.h 又称断言语句,可以看做是功能缩小版的 if 语句,它用于判断某个表达式的 ...

  3. c语言字符操作函数汇总及模拟实现

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 好一段时间没发布文章了,今天为大家分享一些c语言字符串函数.包括strcpy,strcmp,strcat,strncpy,str ...

  4. php php拼接字符串函数_PHP常见字符串操作函数与用法总结

    本文实例讲述了PHP常见字符串操作函数与用法.分享给大家供大家参考,具体如下: 一.字符串的格式化 1.字符串的格式化 trim()函数可以去除字符串的开始位置和结束位置的空格,并将结果字符串返回,默 ...

  5. python字符串函数split_Python常见字符串操作函数小结【split()、join()、strip()】

    本文实例讲述了Python常见字符串操作函数.分享给大家供大家参考,具体如下: str.split(' ') 1.按某一个字符分割,如'.' >>> s = ('www.google ...

  6. strlen,strcpy,strcmp,strcat,strncpy,strncmp,strncat

    写在前面:本文比较长,覆盖了c语言中较多的字符串函数,作者会先带各位了解标准库对函数的介绍,然后模拟实现相关功能. strlen 求字符串长度 size_t表明返回一个无符号类型的数字,是因为考虑到l ...

  7. C语言——常见的字符串函数+内存操作函数的介绍及实现

    文章目录 前言 一.字符串函数 1.求字符串长度 strlen 2.长度不受限制的字符串函数strcpy.strcat.strcmp 字符串拷贝 strcpy 字符串追加拷贝 strcat 字符串比较 ...

  8. 字符串转内存c语言,【一起学C】C语言面试题必考:字符串操作函数,内存操作函数实现...

    本帖最后由 奉聪 于 2017-1-25 14:54 编辑 *******前言******* 我数一下,我发了几个帖子,1.2.3? 哈哈哈,没几个哈,感谢吾爱,让我学到了很多,乐于分享,共同进步! ...

  9. 字符串函数实现(strlen,strcpy,strcmp,strcat,strrev)

    声明:以下代码可能并非最佳方法,若有错误疑问欢迎提出!!! strlen函数 #include<stdio.h> #include<string.h>int mylen(cha ...

最新文章

  1. 005_Queue消息模式发送字节消息
  2. python nonetype_python装饰器 ——@符号与“TypeError: ‘NoneType’ object is not callable” | 学步园...
  3. ADO.NET入门教程(一) 初识ADO.NET
  4. 很多女生都这么干!效果就是可以很快换电脑……
  5. C#/.Net开发入门篇(1)——开发工具安装
  6. java常见的定时任务
  7. HDU 6395 Sequence(分段矩阵快速幂)题解
  8. 浪潮服务器风扇转速调节(已解决)
  9. IDM 6.40.11.2 弹窗的解决思路
  10. 麒麟操作系统配置web服务器,银河麒麟服务器设置
  11. excel文件解密打开密码
  12. VennDiagram包绘制Venn图
  13. CHI论文阅读(1)EmoGlass: an End-to-End AI-Enabled Wearable Platform for Enhancing Self-Awareness of Emoti
  14. 企业服务总线(ESB)
  15. solr mysql 多表_现在定位胆单双方法请问solr的配置数据库表时可以同时配置多
  16. Java保留两位小数方法
  17. 为何别人家的会议井然有序?原来是它的功劳
  18. No provider available from registry 127.0.0.1:2181 for service com.ddbuy.ser 解决方法
  19. 电力系统微型计算机继电保护2018,4月全国自考电力系统微型计算机继电保护试卷及答案解析.docx...
  20. 永磁同步电机三相等效电路图_三相永磁同步电机之永磁体的等效

热门文章

  1. 扫频光学相干层析原理(SS-OCT)
  2. 【数字图像处理】MATLAB实现图像旋转
  3. 计算机网络应用赛甘肃省,关于举办第三届“甘肃省大学生创新杯计算机运用能力竞赛”预赛的.doc...
  4. 嵌入式linux开发笔记——Ubuntu的使用
  5. 如何进行测试用例评审
  6. vue项目使用Hbuilder打包苹果IOS-App详细教程
  7. 计算机文档排版的心得体会,排版心得体会-20210523074838.docx-原创力文档
  8. Nginx反向代理解决跨域问题(个人学习总结)
  9. 《RabbitMQ实战》7.warren和Shovel:故障转移和复制
  10. 推荐开源项目计划管理软件 kanboard