字符函数和字符串函数(零基础超详细讲解)

勇敢牛牛,不怕困难!!!


前言

重点介绍字符和字符串的库函数使用及其注意事项
以下库函数均可在(https://www.cplusplus.com)中查看到


思维导图

在这里插入图片描述

1 strlen

1.1 strlen函数的功能及用法

size_t strlen ( const char * str );

函数功能:求字符串长度(\0之前的)

注意事项:
1:字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2:参数指向的字符串必须要以 ‘\0’ 结束。
3:函数的返回值为size_t,是无符号的( 易错 )

<1>函数用法如下
在这里插入图片描述
<2>关于strlen的返回值size_t .

在这里插入图片描述打开vs编译器,双击size_t,按F12即可转到定义。
如上图可知:typedef是重命名,将无符号整型重命名为 size_t 所以 strlen的返回值是无符号整型。

<3>关于strlen的返回值的例题

#include <stdio.h>
#include<string.h>
int main()
{
	if (strlen("abc") - strlen("abcd") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	return 0;
}

若你以为strlen的返回值是有符号整形的话应该打印<号。
正因为strlen的返回值是无符号整形,把-1的补码认为是一个无符号型的数,即非常大的正数。
执行结果
在这里插入图片描述

1.2 strlen函数的模拟实现

思想:采用指针-指针->结果为之间的元素个数

#include<assert.h>
//方法一:计数器方式
size_t my_strlen1(const char* str)
{
	assert(str != NULL);	//断言,str为空时报错。
	size_t count = 0;
	while(*str++ != '\0')
	{
		count++;
	}
	return count;
}
 
//方法二:递归(不能创建临时变量)
size_t my_strlen2(const char* str)
{
	assert(str != NULL);	//断言,str为空时报错。
	
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen2(str + 1);
	}
}
 
//方法三:指针-指针的方式
size_t my_strlen3(const char* str)
{
	assert(str != NULL);	//断言,str为空时报错。
 
	char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end - str;
}

2 strcpy

2.1 strcpy函数的功能及用法

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

函数功能:将source指向的C字符串复制到destination指向的数组中,包括终止空字符(并在该点停止)。

注意事项
1:源字符串必须以 ‘\0’ 结束。
2:会将源字符串中的 ‘\0’ 拷贝到目标空间。
3:目标空间必须足够大,以确保能存放源字符串。
4:目标空间必须可变。

<1>函数用法如下
在这里插入图片描述
<2>目标空间必须可变
例如:

int main()
{
	//错误代码:arr是常量字符串,不能被修改
	const char* arr = "abcdert";    
	const char* p = "abcd";
	strcpy(arr, p);
	printf("%s", arr);

	return 0;
}

上述代码无法运行,因为arr为常量字符串,常量是不能被修改的!

2.2 strcpy函数的模拟实现

char* my_strcpy(char* dest, const char* sorc)
{
	//只要解引用s了,就需要在前面加上assert防止其空指针
	assert(dest);
	assert(sorc);
	//while (*dest = *sorc)  //先赋值,再判断的,\0也拷贝进去了
	//{
	//	dest++;
	//	sorc++;
	//}
	
	char* ret = dest;
	while (*dest++ = *sorc++)  //这样更好,后置++,先赋值,再++
	{
		;
	}
	//return dest;   此时dest已经改变了,不能直接返回
	return ret;
}

int main()
{
	char arr[10] = "xxxxxxxxx";
	const char* p = "abcdefg";

	printf("%s", my_strcpy(arr, p));

	return 0;
}

3 strcat

3.1 strcat函数的功能及用法

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

函数功能:将源字符串的副本追加到目标字符串。目标中的终止空字符被源中的第一个字符覆盖,空字符包含在由目标中的两个字符串联而成的新字符串的末尾。

注意事项:
1:源字符串必须以 ‘\0’ 结束。
2:目标空间必须有足够的大,能容纳下源字符串的内容。
3:目标空间必须可修改。

<1>函数用法如下
在这里插入图片描述

3.2 strcat函数的模拟实现

思路:先找到目标空间的\0,再追加

char* my_strcat(char* dest, const char* sorc)
{
	//先找\0
	assert(dest && sorc);
	char* cur = dest;
	while (*cur !='\0')
	{
		cur++;
	}
	//再追加
	while (*cur++ = *sorc++)
	{
		;
	}
	return dest;
}

int main()
{
	char arr[10] = "hello ";
	const char* p = "bit";
	printf("%s", my_strcat(arr, p));  //函数的链式访问,将一个函数的返回值作为参数传递给另外一个函数

	return 0;
}

strcat模拟实现的代码思考一个问题,能不能自己给自己追加?

其实是不可以的,他有一定的缺陷,例如 arr1[20]="we"自己给自己追加时,此时其在内存中存储的是w e \0
我们知道strcat是将源头的数据拷贝(包含源头的\0)到目标空间,目标空间的\0会被覆盖。
此时源头数据为w e \0,将其拷贝的话,w将覆盖\0e继续拷贝,变成w e w e,此时我们再拷贝源头的\0时发现已经被覆盖了,这回导致没有结束标志,会死循环

4 strcmp

4.1 strcmp函数的功能及用法

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

函数功能:字符串比较函数,这个函数开始比较每个字符串的第一个字符(ASCII值)。如果它们彼此相等,它将继续处理后面的字符对,直到字符不同或到达终止空字符。

参数:
const char * str1:要比较的 C 字符串。
const char *str2:要比较的 C 字符串

返回值:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
比较对应位置的字符,比较他们的ASCⅡ值,如果相同就比较下一个,直到分出大小。

<1>函数用法如下

//使用strcmp()
int main()
{
	//char arr1[10] = "abcd";
	//char arr2[10] = "abq";

	char arr1[10] = "abcd";
	char arr2[10] = "abcd";

	if (strcmp(arr1, arr2) > 0)
	{
		printf("arr1>arr2\n");
	}
	else if (strcmp(arr1, arr2) < 0)
	{
		printf("arr1<arr2\n");
	}
	else
		printf("arr1=arr2\n");

	printf("%d\n", strcmp(arr1, arr2)); //打印返回值

	return 0;
}

在这里插入图片描述

可以看到arr1和arr2一模一样,故结果为arr1=arr2,且最终打印的返回值为0。

4.2 strcmp函数的模拟实现

思路:写一个循环,条件为两个字符是否相等,相等则为真,进入循环,地址++,不相等,则返回退出循环的地址处的两个值做差。其中,如果两个字符串一直到结束位置都相等,则在循环内部返回0,表示两个字符串相等

// 退出循环条件
// 1:两个不相等时,退出后将两个地址处的元素相减(结果是ASCII值),
// >0表示arr1>arr2;
// <0表述arr1<arr2;
// =0表示arr1=arr2
//2:两个都走到头了,这个时候直接返回0,因为一直都相等
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)  //只有相等才会进来,当都为\0的时候还会进入,判断一下
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

int main()
{
	char arr1[10] = "abcd";
	char arr2[10] = "abq";

	//char arr1[10] = "abcd";
	//char arr2[10] = "abcd";

	if (my_strcmp(arr1, arr2) > 0)
	{
		printf("arr1>arr2\n");
	}
	else if (my_strcmp(arr1, arr2) < 0)
	{
		printf("arr1<arr2\n");
	}
	else
		printf("arr1=arr2\n");

	printf("%d\n", my_strcmp(arr1, arr2));

	return 0;
}

小总结

我们上面这些函数strcmpstrcpystrcat,这些函数都必须依赖\0,都看\0来进行他们的功能
所以我们叫这些函数是长度不受限制的字符串,不关注对几个字符进行拷贝,追加,和比较。
因此还存在一些函数是长度受限制的字符串,能对几个字符进行拷贝,追加,和比较。
比如strncmpstrncpystrncat,下面我们就介绍这些函数。

5 strncpy

5.1 strncpy函数的功能及用法

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

函数功能:由上面的strcpy函数可知这是字符串拷贝函数,但是比他多了一个num,是长度受限制的字符串函数,比如:拷贝2个字符,不需要看 \0。其拷贝方法与 strcpy相同。

注意事项:
1:拷贝num个字符从源字符串到目标空间。
2:如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

3:目标空间必须足够大,以确保能存放源字符串。

<1>函数用法如下

int main()
{
	char arr1[10] = "abcdefg";
	char arr2[10] = "xxx";
	printf("%s\n", strncpy(arr1, arr2, 4));

	return 0;
}

在这里插入图片描述

解析:由于strncpy(arr1, arr2, 3),设置的为3个字节,即,将arr2中的xxx拷贝到arr1中,不包括\0。

5.2 strncpy函数的模拟实现

思路:首先要有一个循环将*dest++ = *src++,还要有一个count去记录拷贝了几个字节的数据,拷贝一次count–
退出循环条件
1:count=0,说明指定的字节拷贝完了,此时退出循环即可
2:src=\0了,此时也要退出循环,因为函数介绍里说如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
此时还需要一个条件判断count是否为0,不为0,再写一个while循环,将\0赋值给
dest,同时–count

char* my_strncpy(char* dest, const char* src, int count)
{
	assert(dest && src);
	char* ret = dest;
	while (count && (*dest++ = *src++) && *src != '\0')   //这里**dest++ = *src++的次数比count多一次
	{
		count--;
	}
	if (count)
	{
		while (--count)
		{
			*dest = '\0';
		}
	}
	return ret;
}

6 strncat

6.1 strncat函数的功能及用法

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

函数功能:将源的前num个字符追加到目标,加上一个终止空字符(源字符串后会自动加一个\0)。 如果source中C字符串的长度小于num,则只复制终止空字符之前的内容可以自己给自己追加了!!!

函数用法如下

int main()
{
	char arr1[] = "abcdefg\0qqqqqq";
	char arr2[] = "xxxx";
	//printf("%s\n", strncat(arr1, arr2, 3));
	printf("%s\n", strncat(arr1, arr1, 3));

	return 0;
}

在这里插入图片描述

7 strncmp

7.1函数的功能及用法

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

函数功能:// 将C字符串str1的最多num个字符与C字符串str2的字符进行比较。 这个函数开始比较每个字符串的第一个字符。
如果它们彼此相等,它继续处理后面的字符对,直到字符不同,直到到达终止空字符,或者直到num个字符在两个字符串中匹配,以先发生的为准。

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

函数用法如下

int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcd";
	printf("%d\n", strncmp(arr1, arr2, 4));
	printf("%d\n", strncmp(arr1, arr2, 5));
	return 0;
}

在这里插入图片描述
解析: 可以看到当比较4个字节时,arr1=arr2,故结果为0
当比较5个字节时,arr1的第5个字节为earr2的第5个字节为\0eASCII值大于\0ASCII值,故结果为大于0的数。

7.2函数的模拟实现

思路:和strcmp实现大致相同,只是限制条件有些许不同
strcmp:元素不等时,有一个为\0是退出
strncmp:元素不等时,num减为0时

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);
	while (num && *str1 == *str2)
	{
		num--;
		if (num != 0)
		{
			str1++;
			str2++;
		}
	}
	return *str1 - *str2;
}

int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcd";
	printf("%d\n", my_strncmp(arr1, arr2, 4));
	printf("%d\n", my_strncmp(arr1, arr2, 5));
	return 0;
}

8 strstr

8.1函数的功能及用法

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

函数功能在一个字符串找另外一个字符串
返回指向str1中第一个str2的指针,如果str2不是str1的一部分,则返回空指针。匹配过程不包括终止的空字符,但是它在那里停止。

函数用法如下

在这里插入图片描述

8.1函数的模拟实现

char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = str1;
	const char* s2 = str2;
	char* p = str1;
	while (*p)  //p找到后记录 子字串 第一次出现的位置
	{
		s1 = p;
		s2 = str2;
//跳出下面这个循环有3种情况
//1:(*s1)!=(*s2)
//2:如果找到了,还会进入循环,s2++后会指向\0,两个可能相等(当s1处于末尾时会相等,再次进入循环++时,会越界),这时应该跳出循环
//3:当s1为\0,或者s2为\0时候。当s1为\0时跳出,说明到头了(可能找到,也可能没找到)。 s2为\0时候说明找到了!!!!

		while ((*s1 != '\0') && (*s2 != '\0') && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)p;
		}
		p++;   
	}
	return NULL;  //如果*p到最后了还没找到

}

8 strtok

8.1函数的功能及用法

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

**函数功能:**这个函数有点不好理解,其实可以理解成字符串分割函数,将一个字符串通过分割符分割成几个单个的字符,从而得到这几个单个的字符,但是需要注意的是使用 strtok会改变原字符串,所以一般先将字符串拷贝,然后将拷贝的字符串进行分割。
比如:现在有一个字符串 123456@QQ.com分隔符是 '@和.'就可以使用这个函数将字符串分割成 123456, QQ , com,三个字符串。
但是需要注意的一点就是:再找第二个分割字符串时,他已经将第一个字符串的分割符变为 \0,这样打印时就能直接打印,所以在分割第二个字符串时,传空指针直到找到下一个分割符。

函数的参数char * str, const char * delimiters ,第一个字符指针指向是要被分割的字符的地址。而第二个字符指针指向分隔符的地址。

返回值strtok的返回值是 char * ,返回一个字符型指针,返回是分割字符串的起始位置

函数用法如下
在这里插入图片描述

代码很罗嗦,如分隔很多的话,要点用很多次,考虑用for循环改进

改进后的代码:
在这里插入图片描述

8.2函数注意事项

1、delimiters参数是个字符串,定义了用作分隔符的字符集合。
2、第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
3、strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针
(注: strtok函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4、 strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记, strtok 函数将保存它在字符串 中的位置。
5、 strtok 函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
6、如果字符串中不存在更多的标记,则返回 NULL 指针。

9 strerror

9.1函数的功能及用法

char * strerror ( int errnum );

函数功能:返回错误码,所对应的错误信息。我们平常上网遇到的404就是错误码。这个错误码就是c语言自己规定的

返回值:指向描述错误的错误字符串的指针

函数用法如下:我们要了解 errno, errno就是将错误信息记录到错误码里的东西,是C语言提供的库函数的全局变量。

1:错误码含义展示
在这里插入图片描述

2:具体应用场景
在这里插入图片描述

补充:perror,也是打印错误信息的函数其实相当于 printf+strerror.
在这里插入图片描述

10 字符分类函数

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母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任何可打印字符,包括图形字符和空白字符

11 memcpy

11.1函数的功能及用法

void * memcpy ( void * destination, const void * source, size_t num );

函数功能:
其实这个函数很好理解,可以从字面上理解,memory+copy,就是内存拷贝函数,也是内存函数。和strcpy函数的区别:emcpy的函数参数是 void*类型,说明他什么都能拷贝,而strcpy不一样,只能拷贝字符串。

函数参数:
因为是只是移动,源头参数都不能修改,所以用const修饰。
size_t就是拷贝 多少字节个数,这个很重要。如果拷贝一个整型,那么就是4个字节

函数的返回值:
memcpy的返回值是void*,因为不知道返回的是什么类型所以我们用 void*类型接收。

函数用法如下
在这里插入图片描述
可以看到,memcpy可以拷贝任意数据类型

11.2函数的模拟实现

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);  
	void* ret = dest;
	while (num--)
	{
		*((char*)dest)++ = *((char*)src)++;   //有的编译器这样的代码跑不过去

		//这样的标准
		//*((char*)dest) = *((char*)src);
		//dest = (char*)dest + 1;
		//src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9};  
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 12);  //将345拷贝到123位置上

	return 0;
}

运行结果:
在这里插入图片描述

注意:
如果我们想自己拷贝自己几个字符,你觉得用上面这个代码合适吗。
显然这个是不太合适的。当我们自己拷贝自己时,可能会改变我们需要拷贝的字符,进而我们最后拷贝的字符就变成了拷贝完的字符。
所以memcpy一般是不能拷贝重叠的字符,但是在vs编译器下,比较智能,可以实现但是在其他编译器上就有可能不会实现
因此就又有一个函数memmove,内存移动函数,就可移动重叠的字符。

12 memmove

12.1函数的功能及用法

void * memmove ( void * destination, const void * source, size_t num );

函数功能:
其实这个函数很好理解,可以从字面上理解,memory+move,就是内存移动函数,也是内存函数。如果源空间和目标空间出现重叠,就得使用 memmove函数处理

函数参数:
因为是只是移动,源头参数都不能修改,所以用const修饰。
size_t就是拷贝 多少字节个数,这个很重要。如果拷贝一个整型,那么就是4个字节

函数的返回值:
memcpy的返回值是void*,因为不知道返回的是什么类型所以我们用 void*类型接收。

函数用法如下
在这里插入图片描述

12.2函数的模拟实现

思路:分2种情况拷贝
1:dest<src
2:dest>=src

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = (char*)dest;
	if (dest < src) //前->后	{
	{
		while (num--)  
		{
			*((char*)dest) = *((char*)src);
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else  //后->前
	{
		while (num--)   //先使用再--,这样下面传的就是--后的数据了
		{
			*(((char*)dest)+num) = *(((char*)src)+num);
		}
	}
	return ret;
}
int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9};
	my_memcpy(arr1+2, arr1, 20);   //将12345拷贝到34567位置上
	//memmove(arr1 + 2, arr1, 20);

	//float arr1[] = { 1.0f,2.0f,3.0f,4.0f };
	//float arr2[20] = { 0 };
	//my_memcpy(arr2, arr1, 8);

	return 0;
}

运行结果:
在这里插入图片描述

因此:
memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用 memmove函数处理。

13 memcmp

13.1函数的功能及用法

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

函数功能:
其实这个函数很好理解,可以参考memcpy,相当于字符串比较的拓展,就是对于什么都可以比较,也是内存函数,比较方法和strcmp比较类似。

函数参数:
因为是只是移动,源头参数都不能修改,所以用const修饰。
size_t就是拷贝 多少字节个数,这个很重要。如果拷贝一个整型,那么就是4个字节

函数的返回值:
memcmy的返回值是 int,和strcmp的返回一模一样

函数用法如下
在这里插入图片描述

14 memset

14.1函数的功能及用法

void * memset ( void * ptr, int value, size_t num );

函数功能:
其实这个函数很好理解,memset,就是内存设置函数,将指定的内存设置为指定的值。将 ptr 指向的内存块的第一个字节数设置为指定的值(解释为无符号字符)

函数参数:
int value就是要将内存设置成什么类型,可以是任意类型,不一定要int
size_t就是拷贝 多少字节个数,这个很重要。如果拷贝一个整型,那么就是4个字节

函数的返回值:
memset的返回值是void*,因为不知道返回的是什么类型所以我们用 void*类型接收。

函数用法如下
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值