【C语言详解】——常见字符和字符串函数及其模拟实现

本文主要介绍一些常见的字符和字符串函数及其模拟实现

所需要的头文件 #include<string.h>

目录

1、求字符串长度 strlen

 1.1模拟实现 strlen(三种方法)

2、长度不受限制的字符串函数

2.1 字符串复制 strcpy

2.1.1 模拟实现 strcpy 

2.2 字符串连接 strcat

2.2.1 模拟实现 strcat

2.3 字符串比较 strcmp

2.3.1 模拟实现 strcmp

3、长度受限制的字符串函数(推荐使用)

3.1 指定长度的字符串复制 strncpy 

3.1.1 模拟实现 strncpy  

3.2 指定长度的字符串连接 strncat 

3.2.1 模拟实现 strncat

3.3 指定长度的字符串比较 strncmp

4、字符串查找函数

4.1  字符串查找 strstr

4.1.1 模拟实现 strstr 

4.2 字符串分割 strtok

5、错误信息报告 strerror

6、字符分类函数

7、内存操作函数

7.1 内存拷贝 memcpy

7.1.1 模拟实现 memcpy

7.2 内存区域复制 memmove

7.2.1 模拟实现 memmove

7.3 按字节比较 memcmp

7.4 内存设置函数 memset


1、求字符串长度 strlen

size_t strlen ( const char * str );
功能介绍:求字符串长度(string length)
注意事项:字符串以'\0'作为结束的标志,strlen函数返回的是字符串中'\0'前出现的字符个数(不包含'\0'),且传参的字符串必须以'\0'结束。
注意函数的返回值为size_t (无符号的)。

 1.1模拟实现 strlen(三种方法)

//1.计数器方法
size_t my_strlen(const char* str)  
{
	assert(str);   //不能为空指针
	int count = 0;
	while (*str != '\0')
	{
		sum++;
		str++;
	}
	return count;
}

//2.指针-指针
size_t my_strlen(char* str)
{
	assert(str);
	char* p = str;
	while (*p != '\0')
	{
		p++;
	}
	return p-str;
}

//3.递归的方式
size_t my_strlen(char* str)
{
	assert(str);
	if (*str != '\0')
	{
		return 1 + my_strlen(str+1);
	}
	else
	{
		return 0;
	}
}

//主函数
int main()
{
	char arr[] = "abcdef";
	size_t ret = my_strlen(arr);  //unsigned int = size_t
	printf("%u\n", ret);
	return 0;
}

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

2.1 字符串复制 strcpy

char * strcpy ( char * destination, const char * source );
功能介绍:字符串复制(string copy)把含有'\0'结束符的字符串复制到另一个地址空间。
注意事项:src和dest所指内存区域不可以重叠,且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。 源字符串必须以 '\0' 结束,会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大且可变,以确保能存放源字符串。

2.1.1 模拟实现 strcpy 

void my_strcpy(char* dest, char* src)
{
	assert(dest && src);  //不能为空指针
	char* ret = dest;
	while (*dest++ = *src++)
		;
}
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

2.2 字符串连接 strcat

char * strcat ( char * destination, const char * source );
功能介绍:字符串连接(string catenate)把两个char类型连接。
注意事项:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。
要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
源字符串必须以 '\0' 结束。目标空间必须有足够大且可变,能容纳下源字符串的内容。

2.2.1 模拟实现 strcat

void my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	//1.找到目标空间的末尾\0
	while (*dest != '\0')
	{
		dest++;
	}
	//2.拷贝字符串
	while (*dest++ = *src++)
		;
}
 
int main()
{
	char arr1[20] = "hello ";
	my_strcat(arr1, "world");
	printf("%s\n", arr1);
	return 0;
}

2.3 字符串比较 strcmp

int strcmp ( const char * str1, const char * str2 );
功能介绍:字符串比较(string compare)
当s1<s2时,返回为数;当s1=s2时,返回值= 0;当s1>s2时,返回数。 
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:1."A"<"B"          2."A"<"AB"          3."compare"<"computer"
注意事项:该函数只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。

2.3.1 模拟实现 strcmp

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;   //相等
		str1++;
		str2++;
	}
	//不相等
	if (*str1 > *str2)
		return 1;
	else
		return -1;
	//简便写法:return (*str1 > *str2); 
}

int main()
{
	char arr1[20] = "zhangsan";
	char arr2[20] = "zhangsanfeng";
	int ret = my_strcmp(arr1, arr2);
	if (ret < 0)
		printf("<\n");
	else if (ret == 0)
		printf("==\n");
	else
		printf(">\n");
	return 0;
}

3、长度受限制的字符串函数(推荐使用)

3.1 指定长度的字符串复制 strncpy 

char * strncpy ( char * destination, const char * source, size_t num );
功能介绍:把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest注意事项:
1)source串长度<=dest串长度(这里的串长度包含串尾NULL字符)
2)source串长度>dest串长度
    综上,一般情况下,使用strncpy时,建议将n置为dest串长度(除非你将多个source串都复制到dest数组,并且从dest尾部反向操作),复制完毕后,为保险起见,将dest串最后一字符置NULL,避免发生在第2)种情况下的输出乱码问题。当然,无论是strcpy还是strncpy,保证dest串容量(能容纳下source串)才是最重要的。

3.1.1 模拟实现 strncpy  

#include<stdio.h>
#include<assert.h>
void my_strncpy(char* dest, char* src, size_t n)
{
	assert(dest && src);  //不能为空指针
	int i = 0;
	char* ret = dest;
	while (*dest)
	{
		*dest++ = *src++;
		i++;
		if (i == n)
			break;
	};
}
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	my_strncpy(arr2, arr1, 5);
	printf("%s\n", arr2);
	return 0;
}

运行截图: 

3.2 指定长度的字符串连接 strncat 

char * strncat ( char * destination, const char * source, size_t num );
功能介绍:把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
注意事项:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。strncat将会从字符串src的开头拷贝n个字符到dest字符串尾部,dest要有足够的空间来容纳要拷贝的字符串。如果n大于字符串src的长度,那么仅将src指向的字符串内容追加到dest的尾部。
strncat会将dest字符串最后的'\0'覆盖掉,字符追加完成后,再追加'\0'。

3.2.1 模拟实现 strncat

void my_strncat(char* dest, const char* src, size_t n)
{
	assert(dest && src);
	char* ret = dest;
	int i = 0;
	//1.找到目标空间的末尾\0
	while (*dest != '\0')
	{
		dest++;
	}
	//2.拷贝字符串
	while (*dest++ = *src++)
	{
		i++;
		if (i == n)
		break;
	}
}

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

 运行截图:

3.3 指定长度的字符串比较 strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
功能介绍: str1 和 str2 进行比较,最多比较前 n 个字节。
当s1<s2时,返回为数;当s1=s2时,返回值= 0;当s1>s2时,返回数。

4、字符串查找函数

4.1  字符串查找 strstr

const char * strstr ( const char * str1, const char * str2 );
      char * strstr (       char * str1, const char * str2 );
功能介绍:用于判断字符串str2是否是str1的子串。
如果是,则该函数返回str1字符串从str2第一次出现的位置开始到 str1结尾的字符串;
否则,返回NULL。

4.1.1 模拟实现 strstr 

思路:

//1.暴力求解
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1; //待匹配原串的指针
	const char* s2 = str2; //待匹配子串的指针
	const char* p = str1;  //匹配位置开始的指针

	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}

int main()
{
	//char arr1[] = "hins@qq.com";
	//char arr2[] = "qq";
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}
//2、KMP匹配算法 略

4.2 字符串分割 strtok

char * strtok ( char * str, const char * delimiters );
功能介绍:分解字符串(string token)为一组字符串。s为要分解的字符串,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。strtok用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
注意事项:strtok函数会破坏被分解字符串的完整,调用前和调用后的s已经不一样了.所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。
//使用方法
int main()
{
	const char* sep = "@.";
	char email[] = "hins@qq.com.net";
	//char* ret = my_strstr(arr1, arr2);
	char cp[30] = { 0 };
	strcpy(cp, email);

	char* ret = NULL;
	for (ret = strtok(cp, sep); 
		 ret != NULL; 
		 ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}

    //下面的版本太啰嗦
	//char* ret = strtok(cp, sep);
	//printf("%s\n", ret);

	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);

	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);

	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);

	return 0;
}

运行截图:

5、错误信息报告 strerror

char * strerror ( int errnum );
通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。必须包含的头文件:#include <errno.h>    #include <string.h>
返回错误码,所对应的错误信息。
//示例代码
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
   FILE *fp;
   fp = fopen("file.txt","r");
   if( fp == NULL ) 
   {
      printf("Error: %s\n", strerror(errno));
   }
  return(0);
}

 运行截图:

示例错误码: 

6、字符分类函数

需要头文件 #include<ctype.h>

函数 如果他的参数符合下列条件就返回真
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字符转大写

7、内存操作函数

7.1 内存拷贝 memcpy

void * memcpy ( void * destination, const void * source, size_t num );
功能介绍:内存拷贝(memory copy)从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.
注意事项:
1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。
2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
3. void*又称泛型指针,什么类型都可以接收。

注意:source和destin都不一定是数组,任意的可读写的空间均可。

7.1.1 模拟实现 memcpy

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--) 
	{
		*(char*)dest = *(char*)src;   //一个字节一个字节的拷贝比较保险
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 0,1,2,3,4,5,6,7 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 28);
	return 0;
}

7.2 内存区域复制 memmove

void * memmove ( void * destination, const void * source, size_t num );
功能介绍:由src所指内存区域复制num个字节到dest所指内存区域memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
注意事项:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

 原理分析:

7.2.1 模拟实现 memmove

void* my_memmove(void* dest, const void* src, size_t 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--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 ,8,9,10 };
	my_memmove(arr1+2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 运行截图:

7.3 按字节比较 memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能介绍:字节比较函数(memory compare)把存储区 str1 和存储区 str2 的前 n 个字节进行比较。该函数是字节比较的,位于string.h。
  • 如果返回值 < 0,则表示 str1 小于 str2。

  • 如果返回值 > 0,则表示 str2 小于 str1。

  • 如果返回值 = 0,则表示 str1 等于 str2 

7.4 内存设置函数 memset

void * memset ( void * ptr, int value, size_t num );
功能介绍:内存设置(memory set)是将某一块内存中的内容全部设置为指定的值,这个函数通常为新申请的内存做初始化工作。(以字节为单位初始化)

OK,以上就是本期知识点“常见字符和字符串函数及其模拟实现”的知识啦~~,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值