字符函数和字符串函数

目录

前言

一、字符串函数

1.strlen

strlen模拟实现

2.strcpy

strcpy模拟实现

3.strcat

strcat模拟实现

4.strcmp

strcmp模拟实现

5.strncpy

strncpy模拟实现

6.strncat

strncat模拟实现

7.strncmp

strncmp模拟实现

8.strstr

strstr模拟实现

9.strtok

strtok模拟实现

10.strerror

二、字符分类函数

三、字符转换函数

四、内存函数

1.memcpy

memcpy模拟实现

2.memmove

memmove模拟实现

3.memcmp

小结


  • 个人主页:库库的里昂
  •  CSDN新晋作者
  •  欢迎 点赞✍评论⭐收藏
  • ✨收录专栏:C语言进阶
  • ✨其他专栏:代码小游戏、C语言初阶、C语言每日一练
  • 希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!

前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数。

一、字符串函数

1.strlen

函数介绍:

size_t strlen ( const char * str );

函数功能:

求字符串的长度 

  • 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
  • 含 '\0' )。
  • 参数指向的字符串必须要以 '\0' 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 )
  • 学会strlen函数的模拟实现

strlen模拟实现

#include
#include
#include

size_t my_strlen(const char* p)
{
	int count = 0;
	assert(p != NULL);//如果等于空指针就会报错
	while((*p) != '\0')
	{
		count++;
		p++;
	}
	return count;
}
int main()
{
	char arr[] = "CSDN";
	int ret = my_strlen(arr);
	printf("%d", ret);
	return 0;
}

2.strcpy

函数介绍:

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

函数功能:

字符串拷贝函数,把源字符串拷贝到目标空间

注意事项:

  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

strcpy模拟实现

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3.strcat

函数介绍:

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

函数功能:

字符串追加函数,将源字符串追加到目标字符串后面,目标中的终止字符’\0’会被源字符串的第一个字符覆盖

注意事项:

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何?

strcat模拟实现

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间的'\0'
	while (*dest != '\0')
	{
		dest++;
	}
	//追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

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

4.strcmp

函数介绍:

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

函数功能:

字符串比较函数

注意事项:

  • 这里比较的不是两个字符串的长度,而是对应位置上的ASCII值
  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

strcmp模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)//如果相等就进去,两个指针加加,但是可能会出现两个字符串相等的情况,两个指针都指向'\0',此时比较就结束了
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

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

5.strncpy

函数介绍:

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

函数功能:

长度受限的字符串拷贝函数

注意事项:

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

strncpy模拟实现

char* my_strncpy(char* dest, const char* src, int num)
{
	assert(dest && src);
	char* ret = dest;
	while (num)
	{
		if (*src == '\0')//此时说明src指针已经指向了待拷贝字符串的结束标志'\0'处,src指针就不用再++了
		{
			*dest = '\0';
			dest++;
		}
		else
		{
			*dest = *src;
			dest++;
			src++;
		}
		num--;
	}
	return ret;
}

int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
	my_strncpy(arr1, "abcdef", 10);
	printf("%s\n", arr1);
	return 0;
}

6.strncat

函数介绍:

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

函数功能:

长度受限的字符串追加函数

注意事项:

  • 从源字符串的第一个字符开始往后数num个字符追加到目标空间的后面,外加一个终止字符。
  • 如果源字符串的长度小于 num,则仅复制终止字符之前的内容。.

strncat模拟实现

char* my_strncat(char* dest, const char* src, int sz)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间的\0
	while (*dest != '\0')
	{
		dest++;
	}
	//追加
	while (sz)
	{
		*dest++ = *src++;
		sz--;
	}
	*dest = '\0';
	return ret;
}

int main()
{
	char arr1[20] = "abc\0xxxxxxxxxxx";
	my_strncat(arr1, "defjhigk", 3);
	printf("%s\n", arr1);
	return 0;
}

7.strncmp

函数介绍:

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

函数功能:

长度受限的字符串比较函数

注意事项:

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

strncmp模拟实现

int my_strncmp(const char* str1, const char* str2, int sz)
{
	assert(str1 && str2);
	while (sz)
	{
		if (*str1 < *str2)
		{
			return -1;
		}
		else if (*str1 > *str2)
		{
			return 1;
		}
		else if(*str1 == '\0'||*str2 =='\0')//当有一个为'\0',说明比较就可以结束了
		{
			if (*str1 == '\0' && *str2 == '\0')//如果二者都是'\0',说明两个字符串相等
			{
				return 0;
			}
			else if(*str1 =='\0')//如果str1为'\0',说明str1小,str2大
			{
				return -1;
			}
			else//如果src为'\0',说明str1大,str2小
			{
				return 1;
			}
		}
		sz--;
		str1++;
		str2++;
	}
}

int main()
{
	int ret = my_strncmp("abcdef", "abcd", 5);
	printf("%d\n", ret);
	return 0;
}

8.strstr

函数介绍:

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

函数功能:

字符串查找函数

注意事项:

  • 在str1指向的字符串中查找str2指向的字符串。
  • 返回一个指向str1中第一次出现的str2的指针。
  • 如果 str2 不是 str1 的一部分,则返回一个空指针。
  • 匹配过程不包括终止空字符,但它到此为止。

strstr模拟实现

char* my_strstr(char* str1, char* str2)
{
	assert(str1 && str2);
	if (*str2 == '\0')
	{
		return str1;
	}
	char* s1 = str1;
	char* s2 = str2;
	char* cp = str1;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
				s1++;
				s2++;
		}
		if (*s2 == '\0')
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "abbvcbcbbdbbvbnui";
	char arr2[] = "bbvb";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	
	return 0;
}

9.strtok

函数介绍:

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

函数功能:

字符串拆分函数

注意事项:

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

strtok模拟实现

int main()
{
	char arr[] = "[email protected]";
	char* p = "@.";
	char buf[20] = { 0 };
	strcpy(buf, arr);
	char* ret=NULL;
	for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
	return 0;
}

10.strerror

函数介绍:

char * strerror ( int errnum );

函数功能:

把错误码转换成错误信息的函数

注意事项:

  • C语言的库函数在运行的时候,如果发生错误,就会把错误码存在一个变量中,这个变量是:errno
  • 返回的指针指向静态分配的字符串(错误信息字符串)

一些错误码对应的错误信息:

int main()
{
	printf("%s\n", strerror(0));
	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;
}
//结果:
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error

二、字符分类函数

函数         如果它的参数复合下列条件就返回真(一个非零数字)
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       任何可打印字符,包括图形字符和空白字符

三、字符转换函数

tolower   将大写字母转换为小写字母

toupper   将小写字母转换为大写字母

int tolower ( int c );
int toupper ( int c );

注意事项:

  • 如果 c 是小写字母并且具有大写等效字母,则将 c 转换为其大写等效字母。如果无法进行此类转换,则返回的值为 c 不变
  • 这里是值传递,不会改变实参

示例:

int main()
{
	char arr[] = "I Have An Apple";
	int i = 0;
	while (arr[i])
	{
		if (isupper(arr[i]))
		{
			printf("%c", tolower(arr[i]));//把转换的小写字母直接打印出来
		}
		else
		{
			printf("%c", arr[i]);
		}
		i++;
	}
	return 0;
}
//结果:
i have an apple

int main()
{
	char arr[] = "I Have An Apple";
	int i = 0;
	while (arr[i])
	{
		if (isupper(arr[i]))
		{
			arr[i] = tolower(arr[i]);//用转换后的小写字符替换原有的大写字符
		}
		printf("%c", arr[i]);
		i++;
	}
	return 0;
}

以上介绍的这些函数都是针对字符串或者字符的,那如果我们要拷贝其他类型的数据呢?用上面这些函数自然就行不通了,此时就要用到即将介绍的内存函数了。

四、内存函数

1.memcpy

函数介绍:

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

函数功能:

内存拷贝函数

注意事项:
 这里的destination指向要在其中赋值内容的目标数组,source指向要复制的数据源,num是要复制的字节数,注意这里前两个指针的的类型还有函数返回值都是void*,这是因为,memcpy这个函数是内存拷贝函数,它有可能拷贝整型,浮点型,结构体等等各种类型的数据……虽然返回类型是void*,但他也是必不可少的,void*也表示一个地址,用户可以把它强制转换成自己需要的类型去使用。

 

memcpy模拟实现

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)++;
		num--;
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1+2, 16);
	return 0;
}

上面代码中模拟出来的my_memcpy函数已经可以实现把arr1数组中的元素拷贝到arr2数组里面去。但当我们想把arr1数组中的1、2、3、4、5拷贝到arr1数组中的3、4、5、6、7上去时,就会发生错误,如下图:

字符函数和字符串函数_第1张图片

原因是:当1拷贝到3上时,原来的3已经被1替换,当2拷贝到4上的时候,原来的4已将被2替换。所以当拷贝arr[2]到arr[4]上的时候,原本arr[2]里面存放的3已将被1替换了,同理,所以才得出了不符合我们预期的结果。那如何解决这个问题呢?先来分析这个问题产生的原因,这是因为源空间与目标空间之间有重叠,这里的arr[2]、arr[3]、arr[4]既是源空间也是目标空间,当拷贝1和2的时候把源空间中开没有拷贝的3和4就给覆盖了,此时源空间arr[2]和arr[3]里面存的就不再是3和4了,而是1和2,所以此时拷贝arr[2]和arr[3]里面的数据,其实拷贝的就是1和2。为了解决这个问题,我们可以从后往前拷贝,此时就不会出现这样的问题
字符函数和字符串函数_第2张图片

解决的思路就是:先拷贝重叠区域的元素,避免其还没拷贝就被覆盖掉。但并不是一遇到源空间和目标空间重叠就从后王前拷贝,比如下面这样,此时要把3、4、5、6、7拷贝到1、2、3、4、5里面
字符函数和字符串函数_第3张图片

此时就不能用上面的方法从后往前拷贝了,但是可以根据上面提到的解决思路:先拷贝重叠区域的元素,所以此时就应该从前往后拷贝,即按照3、4、5、6、7的顺序去拷贝
 因此在拷贝的时候,是按照从前往后的顺序还是从后往前的顺序得看具体的情况。当目标空间的起始位置在源空间的前面时,就得从前往后进行拷贝。当目标空间的起始位置在源空间的后面的时候,就要从后往前拷贝。当源空间和目标空间没有任何重叠的时候,不管是从前往后拷贝还是从后往前拷贝效果都一样。
 接下来介绍的memmove函数就可以完成重叠空间的元素拷贝。

2.memmove

函数介绍:

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

函数功能:

内存拷贝函数

注意事项

它的参数、返回值与memcpy函数一模一样。这里就不过多介绍。对于这两个函数来说,目标空间必须足够大,不然就会发生越界访问。

memmove模拟实现

void* my_memmove(void* dest, const void* src, int num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)//目标空间的地址小,说们目标空间靠前
	{
		//从前向后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//从后往前
		while (num--)//num为1的时候,下面的num就是0
		{
			*((char*)dest + num) = *((char*)src + num);//通过num的减减就可以实现对每一个字节的访问
		}
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1, 20);
	return 0;
}

3.memcmp

函数介绍:

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

函数功能:

内存比较函数

注意事项:

  • 比较从ptr1和ptr2指针开始的num个字节
  • 两个内存块中不匹配的第一个字节在 ptr1 中的值低于 ptr2 中的值返回一个小于零的数子,相等返回零,两个内存块中不匹配的第一个字节在 ptr1 中的值大于在 ptr2 中的值返回一个大于零的数子

小结

上述的这些都是我们经常会在平时的代码中使用到的,熟练掌握起来为我们提供很大的帮助。当然,除此之外,C语言的库函数中还有许多有趣的有关字符和字符串的函数,如果你有兴趣想要了解,可以在下面的网站或软件找到它们,里面都有对每一个函数详细的各个部分的说明、作用的解析和示例,博主就不一一赘述了。

字符函数和字符串函数_第4张图片

你可能感兴趣的:(C语言进阶,算法,java,数据结构,开发语言)