字符串函数的超全面介绍

  • Post author:
  • Post category:其他




字符串函数

对于字符串函数,我大概把他们分成了这样几类,希望能够帮助大家记忆与理解,顺便也能对这几个函数先稍稍熟悉

1.长度不受限制的字符串函数:

strcpy,strcat,strcmp

2.长度受限制的字符串函数:

strncpy,strncat,strncmp

3.字符串查找:

strstr,strtok

4.错误信息报告

strerror



strlen

这个函数的功能是计算字符串的字符数量,它从一个字符串的第一个字符一直计数到’\0’前的字符,比方说我们有这样一个数组

char arr1[] = "abc";

我们知道在引号内的字符串,编译器会自动在它的末尾加上一个’\0’,所以当我们用strlen对该数组计数时,输出结果就是3,又比方说我们有这样一个字符串

char arr2[] = "abc\0def";
int len = strlen(arr2);

那么这时的len等于几呢,我相信大家一定能想出答案,len等于3,因为strlen会接受一个元素的地址,并不断向后寻找计数到’\0’,而在这里c后面就出现了’\0’,所以计数也就到此为止了。值得注意的是,strlen的返回值是size_t类型,即unsigned int类型,是一个无符号的整型,所以当我们看到这样的代码时,我们会发现一个奇怪之处

int main()
{
	char arr[] = "abc";
	char arr1[] = "abcde";
	if ((strlen(arr) - strlen(arr1)) > 0)
	{
		printf("arr is bigger");
	}
	else
	{
		printf("arr1 is bigger");
	}
	return 0;
}

显然arr1的长度应该是大于arr的,所以我们的输出结果理应是arr1 is bigger,但我们会看到这样一个结果

在这里插入图片描述

这就很让人疑惑了,这电脑不是睁着眼睛说瞎话么,arr1明明是肉眼可见的更大,为什么会发生这种情况呢,很简单,问题就出在strlen的返回值类型上,既然它是一个无符号整形数据,那么当两个无符号整形数据相减得到的结果也会是一个无符号整形,为了避免这一种情况,我们也可以编写一个我们自创的my_strlen

int my_strlen(const char* arr)
{
	assert(arr != NULL);
	int count = 0;
	while (arr[count] != '\0')
	{
		count++;
	}
	return count;
}

int main()
{
	char arr[] = "abc";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

这是让我们来运行程序,就会得到我们想要的结果了



strcpy

strcpy函数的功能是拷贝一个字符串的内容到另一个字符串,且strcpy会把’\0’也给拷贝到目标字符串,它的大致格式如下

strcpy(char* dest, char*src);

也就是把逗号之后的字符串给拷贝到前一个字符串中,比方说我有如下代码

int main()
{
	char arr1[20] = "###########";
	char arr2[] = "Hello";
	strcpy(arr1, arr2);
	return 0;
}

让我们来看看它的调试结果:

在这里插入图片描述

我们可以发现,arr2的内容包括’\0’全被拷贝进了arr1中,但是我们也得注意一个问题,当我们遇见了这样的代码:

int main()
{
	char* p = "###########";
	char arr2[] = "Hello";
	strcpy(p, arr2);
	return 0;
}

我们要思考,这里的拷贝是可行的吗?如果不可行,那么不可行的原因是什么?我们会发现这样的报错

在这里插入图片描述

因为在这样的写法中,p后面的是一个常量字符串,p记录的则是常量字符串中第一个元素的地址,所以常量字符串的元素自然不能被改变



strcat

字符串追加(字符串连接)

当我们想要把一串字符追加到字符的后面时,其实我们也有一个库函数可以使用,他就是strcat,它的基本格式跟strcpy一样,也是

strcat(char* dest, char* src);

也就是把逗号后面的字符串追加到逗号前的字符串,我们同样也可以放上来一串代码作为示例

int main()
{
	char arr1[20] = "Hello ";
	char arr2[] = "World";
	strcat(arr1, arr2);
	return 0;
}

我们想把“World”给追加到“Hello”后面,让我们看看结果

在这里插入图片描述

这里我们就很好的把arr2中的字符追加到arr1中了,并且他还将目标字符串中的’\0’给覆盖掉



模拟实现strcat

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);//在这里确保传过来的不是一个空指针
	while (*dest)
	{
		dest++;//首先我们让dest指向'\0'
	}
	while (*((dest++)) = *(src++))
	{
		;//在这里我们把src追加给dest,并在追加完'\0'后自动跳出循环
	}
	return ret;
}

int main()
{
	char arr1[20] = "Hello ";
	char arr2[] = "World";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

那么函数可以给自己追加呢,答案是可以的,但不能用strcat,而要用strncat函数,至于为什么不能用strcat呢,我们可以从这个函数的原理来分析



为什么strcat不能给自己追加

比方说我创建了一个字符串“abcdef”,我要将它给自己追加
在这里插入图片描述

在my_strcat中我们可以看见我们将arr分别传输了给了dest和src,而在第一次循环后,dest指向了’\0’,所以此函数会把src中的a替代’\0’,以至于函数出现死循环



strcmp

它是在比较字符串中字符ascII码值的大小,基本格式为int strcmp(const char* str1, const char* str2);

大小 str1>str2 str1=str2 str1<str2
返回值 >0 =0 <0



模拟实现strcmp

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)//当然我们也可以把这些代码直接写成return *s1 - *s2;
	{
		return 1;//但因为在vs的编译器中返回值均为1,0,-1,所以这里也与vs的返回值对应
	}
	else
	{
		return -1;
	}
}

int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abde";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;



strncpy

这个函数与strcpy的功能差不多,我们在strcpy函数中会发现,这个函数简直是桀骜不驯,比方说我们有两个字符串

arr1[] = "abcde";
arr2[] = "fghijkl";

我们会发现arr1数组只能容纳6个字符,但是当我们想把arr2中的字符串用strcpy拷贝给arr1时,这个函数会毫不犹豫地拷贝过去,并在拷贝之后猜想我们报错,这就很容易造成程序崩溃,所以为了相对安全,就出现了strncpy,他的基本格式为

strncpy(arr1, arr2, num);

在我们可以用num参数确定要拷贝元素的个数也就是说当我们给num传参1时,他就会将arr2中的第一个元素拷贝到arr1中的第一个元素



strncmp

这个函数和strcmp类似,也是用来比较字符串ascII码值大小的函数,但他有一个用来指定比较元素个数的参数,他的基本格式为

int strncmp(const char* s1, const char* s2, num);

他的返回值为整形

大小 str1>str2 str1=str2 str1<str2
返回值 >0 =0 <0

我们可以拿几行代码来参考一下

int main()
{
	char* arr1 = "abcdef";
	char* arr2 = "abcefg";
	int ret1 = strncmp(arr1, arr2, 3);
	int ret2 = strncmp(arr1, arr2, 4);
	printf("%d %d\n",ret1, ret2);
	return 0;
}

我们分别比较了两字符串前3个与4个元素的大小,所以返回值分别为0,-1

在这里插入图片描述



strstr

我们看到这个函数中有两个str,所以很容易理解,这个函数实际上就是在一个字符串中寻找另一个字符串,比方说有一个字符串”abcdef”和”bcd”,显然我们可以在第一个字符串中找到第二个字符串,他的基本格式是

const char* strstr(const char* arr1, const char* arr2);

也就是在arr1寻找arr2,当找到时会返回第一次找到时的地址,找不到时则会返回一个空指针,比方说有这么一串代码

int main()
{
	char* arr1 = "abcdefabcdef";
	char* arr2 = "bcd";
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了: %s", ret);
	}
	return 0;
}

我们可以发现这个函数返回的是arr1中第一次出现arr2时的地址,而如果找不到时则返回一个空指针



模拟strstr的实现

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cp = str1;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		if (*str2 == '\0')
		{
			return (char*)str1;
		}
		while (*s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}
int main()
{
	char* str1 = "abbbbcdef";
	char* str2 = "bbc";
	char* ret = my_strstr(str1, str2);
	if (ret == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了: %s", ret);
	}
	return 0;
}



strtok

这个函数比较抽象,所以笔者会尽量详细地介绍,它的功能是切割字符串,比方说我们有这样一串儿字符”duzhe.doushi.dushuren”,我们想把它给切割成给duzhe,doushi,dushuren三个字符串我们就可以使用这个函数,那么还是老样子,我先展现一下这个函数的基本格式:

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

我先写出这个函数的特性,语言有点生涩,但没关系,笔者会在后面用实例进行解释

1.sep参数是个字符串,定义了用作分隔符的字符集合。

2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标

记。

3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:

strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容

并且可修改。)

4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串

中的位置。

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

记。

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

让我们从一个简单的代码中理解这个函数

int main()
{
	char arr[] = "abc.def@gh";
	char* sep = ".@";
	char* ret = NULL;
	ret = strtok(arr, sep);
	printf("%s\n", ret);
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	return 0;
}

我们现在有一个字符串”abc.def@gh”,想把它分割成abc,def和gh

在这里插入图片描述

我们又有一个名为sep的字符串,这里存在着分割标志.和@,在第一次strtok的调用中,它找到了第一个分隔符’.’,并将它用\0覆盖,并返回了a的地址,保存了这个\0的地址,在第二次调用中,它的第一个参数为NULL,所以他会从上一次保存的地址开始继续向后寻找下一个分隔符@,并重复以上步骤,在第三次调用中,strtok的第一个参数仍为NULL,他又会从记录的地址向后寻找直到\0结束,并返回g的地址,当然我们在实际使用中不会这样写,我们会用以下的写法

int main()
{
	char arr[] = "abc.def@gh";
	char* sep = ".@";
	char* ret = NULL;
	for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL,sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}

我们会发现一件比较讨厌的事情,strtok在每次调用时都会把源字符串中的分隔符给改变成’\0’,所以我们在使用这个函数时分割的往往是这个字符串的临时拷贝,比方说我们可以添加这样的代码

	char arr1[30] = { 0 };
	strcpy(arr1, arr);

并用arr1进行分割操作



strerror

这是我们讲解的最后一个字符串函数,坚持就是胜利!

让我们来想想我们使用库函数时会不会出错呢,答案是会的,而且出错时会返回一个错误码,可是我们看不懂这个错误码是什么意思,这是就需要使用strerror函数

int main()
{
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	printf("%s\n", strerror(5));
	return 0;
}

在这里插入图片描述

下图为输出结果,即每个错误码的对应错误信息



版权声明:本文为JayceSun原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。