c语言strcpystrcmpstrcat等常见字符操作函数的介绍

Posted aaaaaaaWoLan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言strcpystrcmpstrcat等常见字符操作函数的介绍相关的知识,希望对你有一定的参考价值。

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

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'了,说明两字符串相等,返回0
			return 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 be

 strncat (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;<

以上是关于c语言strcpystrcmpstrcat等常见字符操作函数的介绍的主要内容,如果未能解决你的问题,请参考以下文章

C语言入门到精通,这一篇就够了(13万字笔记)

C语言入门到精通,这一篇就够了(13万字笔记)

中文汉字和常见英文数字等的unicode编码范围

万字整理,C语言最全入门笔记!

C语言万字讲解 从零到精通 (文件操作与文件函数)

C语言常见字符串库函数的使用与实现