C语言应用——字符串函数

常用字符串函数模拟

目录

常用字符串函数模拟

0、常用字符串汇总

1:字符串信息提取函数

2:内存操作函数

3:字符串操作函数

4:字符分类与操作函数(分类函数判断条件为真时,返回真(1))

1、字符串信息提取函数

1:strlen

1. 原理 

2. 模拟代码

3. 测试与结果分析

2:strstr

1. 原理

2. 暴力求解法模拟代码和结果测试分析

3. KMP法模拟代码和结果测试分析

2、内存操作函数

1:memcpy

1. 原理

2. 模拟代码

3. 测试与结果分析

2:memmove

1. 原理

2. 模拟代码

3. 测试与结果分析

3:memset

1. 原理

2. 模拟代码

3. 测试与结果分析

4:memcmp

1. 原理

2. 模拟代码

3. 测试与结果分析

3、字符串操作函数

1:strcpy和strncpy

1. 原理

2. strcpy模拟代码和测试

3.strncpy模拟代码和测试

2: strcat和strncat

1. 原理

2. strcat模拟代码和测试

3.strncat模拟代码和测试

3:strcmp和strncmp

1. 原理

2. strcmp模拟代码和测试

3.strncmp模拟代码和测试

5、结束语和一些相似函数的对比

1: 拷贝函数memcpy、memmove、strcpy和strncpy

2: 比较函数memcmp、strcmp和strncmp

3:结束语


0、常用字符串汇总

C语言的字符串都是放在常量字符串字符数组中。

常用字符串函数如下:

1:字符串信息提取函数

(1)strlen :求字符串长度(模拟实现)

(2)strstr:字符串查找(模拟实现)

2:内存操作函数

(1)memcpy:内存拷贝函数(模拟实现)

(2)memmove:内存拷贝函数(模拟实现)

(3)memset:内存初始化函数(模拟实现)

(4)memcmp:内存比较函数(模拟实现)

3:字符串操作函数

(1)strcpy:字符串复制函数(长度不受限制)(模拟实现)

(2)strcat:字符串连接函数(长度不受限制)(模拟实现)

(3)strcmp:字符串比较函数(长度不受限制)(模拟实现)

(4)strncpy:字符串复制函数(长度受限制)(模拟实现)

(5)strncat:字符串连接函数(长度受限制)(模拟实现)

(6)strncmp:字符串比较函数(长度受限制)(模拟实现)

4:字符分类与操作函数(分类函数判断条件为真时,返回真(1))

(1)iscntrl:判断字符是否为控制字符

(2)isspace:判断字符是否为空白字符

(3)isdigit:判断字符是否为十进制数字字符

(4)isxdigit:判断字符是否为十六进制数字字符

(5)islower:判断字符是否为小写字母

(6)isupper:判断字符是否为大写字母

(7)isalpha :判断字符是否为字母(不分大小写)

(8)isalnum:判断字符是否为字母(不分大小写)或数字字符

(9)ispunct:判断字符是否为标点符号(不属于数字字符、字母和图像字符的字符都叫标点字符)

(10)isgraph:判断字符是否为图像字符

(11)isprint:判断字符是否可打印

(12)tolower:将大写字母转化为小写字母(如果字符不是大写字母,则不转换)

(13)toupper:将小写字母转化为大写字母(如果字符不是小写字母,则不转换)

本文对部分字符串信息提取函数、内存操作函数字符串操作函数进行原理讲解和模拟实现。

1、字符串信息提取函数

1:strlen

1. 原理 

函数作用:计算字符串长度(不包括字符串结束符:'\0'

函数结构:size_t strlen( const char *string );

函数参数
参数介绍(源于MSDN)
string目标字符串起始地址。数据类型:const char*
返回值字符串长度。数据类型:int

需要注意的点:strlen碰到'\0'则停止计数。

2. 模拟代码

这里给出递归和计数器方法模拟strlen的代码

计数器模式:

int Str_len(const char *arr)
{
	char* p = arr;
	while (*(p++) != '\0');
	return p - arr - 1;
}

递归方式:

unsigned int Str_len(char* strs)
{

		if (*strs == '\0')
		{
			return 0;
		}
		else
		{
			return 1 + Str_len(strs + 1);
		}
	}

}

3. 测试与结果分析

测试代码如下:

int main()
{
	char arr[] = "abscjdksuslaj asdl";
	printf("%d", Str_len(arr));
	return 0;
}

程序运行结果:

2:strstr

1. 原理

函数作用:查找子字符串

函数结构:char *strstr( const char *string, const char *strCharSet );

函数参数
参数介绍
string目标字符串首地址。参数类型:const char *
strCharSet子字符串首地址。参数类型:const char *
返回值如果目标字符串中存在子字符串,则返回子字符串第一次出现时目标字符串的地址。
函数概述:如果目标字符串中不存在子字符串,则返回NULL;如果子字符串为NULL,则返回目标字符串的首地址。

说白了,strstr就是求解子串的问题。(假设只有一个目标字符串和一个子字符串)

求解思路:(1)暴力求解法(2)KMP算法

(1)暴力求解法

废话不说,直接上图:

暴力求解子串问题

这里需要说明的是:这种方法比较简单,其时间复杂度为O(M*N)。

(2)KMP算法求解

KMP是专门求解子串包含问题的一种算法,其优势在于KMP对子字符串信息挖掘较多。

本人也找过KMP算法的介绍,大多给出一堆公式和大段文字进行讲解,感觉把人将蒙了。这是因为:KMP算法原理不难,但是叙述起来比较麻烦。所以,这里就不叙述了,直接上图。

如果还是感觉过于复杂或看不懂,推荐直接看代码,代码不长,把判断条件看明白就行。另外,本人网上查资料时碰见几个将KMP的博客,这里给出链接。

https://www.cnblogs.com/zhangboy/p/7635627.html

https://mp.weixin.qq.com/s?src=11&timestamp=1609923375&ver=2811&signature=JkII*3KBCxVOWGu9yYO6c60uiAZFSDNIGe4L4E3IsmU7fdtSzxlxJKT3CHyeyi3VPAkwUb9UY6Kqb84uBBKiyoRftixRPpA-K8aXGDcCiyyJzBfAUC9hNmoqkhiewdjd&new=1

2. 暴力求解法模拟代码和结果测试分析

//暴力求解法
char* my_strstr1(const char* string, const char* strcharset)
{
	int i = 0;//目标字符串下标
	int j = 0;//子字符串下标
	if (*strcharset == '\0')
	{
		return string;
	}
	else
	{
		int num = 0;
		char* s = strcharset;
		while (*s++)
		{
			num++;
		}
		while (1)
		{
			if (*(string + i + num - 1) == '\0') //目标字符串到结尾了还没跳出(说明目标字符串不含子串)
			{
				return NULL;
			}
			else
			{
				if (*(strcharset + j) == '\0') //b已全部判断完毕,目标字符串中包含子串
				{
					return string + i;
				}
				else
				{
					if (*(string + i + j) == *(strcharset + j)) //匹配到字符
					{
						j++;
					}
					else
					{
						j = 0;
						i++;
					}
				}
			}
		}
	}
}

测试代码:

int main()
{
	char a[] = "abcdefa";
	char b[] = "def";
	char c[] = "";
	char d[] = "acef";
	printf("测试1\n");
	if (my_strstr1(a, b) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr1(a, b) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, b), *my_strstr1(a, b));
	}
	printf("测试2\n");
	if (my_strstr1(a, c) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr1(a, c) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, c), *my_strstr1(a, c));
	}
	printf("测试3\n");
	if (my_strstr1(a, d) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr1(a, d) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, d), *my_strstr1(a, d));
	}
	return 0;
}

结果分析:分析比较简单,这里直接给出程序运行结果图

3. KMP法模拟代码和结果测试分析

KMP代码:代码比较长是因为我加了很多额外条件判断,KMP的关键代码注释:关键代码

//前后缀法求解Next数组
void KMP_Next(const char* string, int* next)  
{
	*next = -1;
	int k = -1;
	int j = 0;
	while (*(string + j + 1) != '\0') //判断结束
	{
		if (k == -1 || *(string + j) == *(string + k))
		{
			*(next + j + 1) = k + 1;
			k++;
			j++;
		}
		else
		{
			k = *(next + k);
		}
	}
}




//KMP算法模拟strstr
char* my_strstr_KMP(const char* string, const char* strcharset)
{
	//先判断子字符串是否为空?  如果是返回string
	if (*strcharset == '\0')
	{
		return string;
	}

	char* s1 = string;
	char* s2 = strcharset;
	int sit = 0;//报错信息
	//先计算子字符串的长度(因为要定义next数组)
	int num = 0;
	while (*s2++)
	{
		num++;
	}
	int* next = NULL;
	while (next == NULL)
	{
		next = malloc(num * 4);  //动态开辟内存(最后要释放)
		sit++;
		if (sit == 10) //连续10次都无法成功开辟,则报错,直接返回
		{
			perror("无法为NEXT数组开辟内存!");
		}
	}
	//Next数组求解
	KMP_Next(strcharset,next);
	//KMP算法
	int i = 0;
	int j = 0;
	while (*(string + i) != '\0' && *(strcharset + j) != '\0')  //结束标志
	{
		if (j == 0 || *(string + i) == *(strcharset + j))  //KMP关键判断语句(看这里,看这里,看这里,看这里,看这里,看这里,看这里)
		{
			i++;
			j++;
		}
		else
		{
			j = next[j]; //KMP关键判断语句(看这里,看这里,看这里,看这里,看这里,看这里,看这里)
		}
	}
	if (*(strcharset + j) == '\0') //匹配成功,返回地址信息
	{
		free(next);
		next = NULL;
		return string + i - num;
	}
	else if (*(string + i) == '\0')
	{
		free(next);
		next = NULL;
		return NULL; //表示目标字符串不含子串
	}
}

测试代码:

这里单独测试了Next求解函数的正确性,然后分别举例测试KMP

int main()
{
	//测试1:测试前后缀法求解Next数组
	//对于字符串:"DABCDABD " 其Next数组={-1,0,0,0,0,1,2,3};
	char aa[] = "DABCDABD";
	int a_next[8] = {0};
	KMP_Next(aa,a_next);
	printf("测试1\n\n");
	printf("子字符串:DABCDABD 的Next数组元素为: ");
	for (int i = 0; i < 8; i++)
	{
		printf("%d ", a_next[i]);
	}
	printf("\n");
	//测试2:KMP测试
	char a[] = "abcdefa";
	char b[] = "def";
	char c[] = "";
	char d[] = "acef";
	printf("\n测试2\n\n");
	if (my_strstr_KMP(a, b) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr_KMP(a, b) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, b), *my_strstr_KMP(a, b));
	}
	printf("\n测试3\n\n");
	if (my_strstr_KMP(a, c) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr_KMP(a, c) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, c), *my_strstr_KMP(a, c));
	}
	printf("\n测试4\n\n");
	if (my_strstr_KMP(a, d) == &a)
	{
		printf("b为空字符串\n");
	}
	else if (my_strstr_KMP(a, d) == NULL)
	{
		printf("目标字符串中不包含子字符串\n");
	}
	else
	{
		printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, d), *my_strstr_KMP(a, d));
	}
	return 0;
}

程序运行结果为:

2、内存操作函数

1:memcpy

1. 原理

函数作用:内存复制函数。
函数结构为:void *memcpy( void *dest, const void *src, size_t count );

函数参数介绍
参数介绍(源于MSDN)
dest目标内存空间的起始地址。参数类型:void *
src源空间的起始地址。参数类型:const void *
count复制内存空间大小(单位:字节)。参数类型:unsigned int
返回值返回dest的值

函数功能:从源空间起始地址开始,将往后的count个字节的内容复制到目标

内存空间之中。(要求:dest<=src或 src+count<=dest)

需要注意的一点是:使用本函数时,必须保证  dest<=src或 src+count<=dest。

由于memcpy与memmove原理相似,memcpy的原理详解放在了memmove小节中(2.2 memmove。这里只给出模拟代码和测试。

2. 模拟代码

void* my_memcpy(void* dest, const void* src, uint count)
{
	char* des = (char*)dest;
	if (src >= dest || (char*)src + count <= (char*)dest)
	{
		while (count--)
		{
			*(char*)des = *(char*)src;
			des = (char*)des + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		printf("源空间和目标空间有重叠,复制会出错。请使用memmove函数,本函数不会进行复制。\n");
	}
	return dest;
}

3. 测试与结果分析

测试代码为:

int main()
{	
	//测试字符串
	char a[] = "12345678901234567890";
	char b[] = "abcdefghijklmnopqrst";
	char c[] = "abcdefghij1234567890";
	printf("测试情况1\n更改前:%s\n", a);
	my_memcpy(a + 9, a + 4, 5);  //dest>=src+n
	printf("更改后:%s\n", a);
	printf("测试情况2\n更改前:%s\n", b);
	my_memcpy(b, b + 5, 5); //dest<=src
	printf("更改后:%s\n", b);
	printf("测试情况3\n更改前:%s\n", c);
	my_memcpy(c + 4, c, 10);  //src+n>dest>src
	printf("更改后:%s\n", c);
	return 0;
}

输出分析:

(1)对于情况1的测试 

my_memcpy(a + 9,a + 4,5);  //dest>=src+n

从a[4]起始的5个字节的内容("56789")复制到a[9]起始的5个字节的空间(“01234”)。此时dest>=src+n,从低地址往高地址正常复制即可,复制完应为:“123456789(a[0]到a[8]不动)56789(a[9]到a[13]为a[4]到a[8]粘贴的内容)567890(a[14]到a[19]不动)”,故结果为:"12345678956789567890"

(2)对于情况2的测试 

my_memcpy(b, b+5, 5); //dest<=src

从b[5]起始的5个字节的内容("fghij")复制到b[0]起始的5个字节空间("abcde")。此时dest<=src,从低地址往高地址正常复制即可,复制完应为:"fghij(被粘贴的内容)fghijklmnopqrst(后面的不变)",故结果为:"fghijfghijklmnopqrst"

(3)对于情况3的测试 

my_memcpy(c+4, c, 10);  //src+n>dest>src

不满足my_memcpy的使用条件,所以直接报错,并且不对字符串c进行任何操作。

程序运行结果:

2:memmove

1. 原理

函数作用:内存复制函数。

函数结构为:void *memmove( void *dest, const void *src, size_t n);

函数参数介绍
参数介绍(源于MSDN)
dest目标内存空间的起始地址。参数类型: void *
src源空间的起始地址。参数类型:const void *
n复制内存空间大小(单位:字节)。参数类型:unsigned int
返回值返回dest的值
函数功能:从源空间起始地址开始,将往后的count个字节的内容复制到目标内存空间之中。

内存复制之中存在以下三种情况:

问题:为什么情况1和情况2对memmove来说是一样的?

因为memmove是将源空间的内容复制到目标空间,那么对于源空间来说,源空间没有完成复制的空间是不允许被更改的。就比如对于情况3来说:一开始,源空间src地址处的内容会被复制,并粘贴到目标空间的dest地址处。但dest地址也属于源空间,并且此时dest地址处的内容并未被复制,所以这种行为是不被允许的。对于情况2来说,虽然源空间和目标空间也有重叠,但目标空间中src地址处空间被粘贴内容时,源空间src地址处的内容已经被复制完成了,所以没有影响。(简而言之:如果一块空间既属于源空间,有属于目标空间,那么这块空间的内容在被复制之前,不可对其进行粘贴操作

怎么解决?

我们要注意到的是:传统意义上的复制和粘贴都是从低地址开始的,这是因为函数参数就是源空间和目标空间的起始地址。但对于情况3而言,这种从低地址到大地址的复制方法不可行,那么就反其道而行,从大地址往小地址进行复制(即对于情况3而言:首先复制src+n地址处的内容,粘贴到dest+n中,然后往低地址进行复制和粘贴操作

2. 模拟代码

void* my_memmove(void* dst, const void* src, uint count)
{
	void* ret = dst;
	if (dst <= src || (char*)dst >= ((char*)src + count)) {
		//情况1和情况2
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else {
		//内存空间重叠
		dst = (char*)dst + count - 1;
		src = (char*)src + count - 1;
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst - 1;
			src = (char*)src - 1;
		}
	}
	return(ret);
}

3. 测试与结果分析

int main()
{
	//测试字符串
	char a[] = "12345678901234567890";
	char b[] = "abcdefghijklmnopqrst";
	char c[] = "abcdefghij1234567890";
	printf("测试情况1\n更改前:%s\n", a);
	my_memmove(a + 9,a + 4,5);  //dest>=src+n
	printf("更改后:%s\n",a);
	printf("测试情况2\n更改前:%s\n", b);
	my_memmove(b, b+5, 5); //dest<=src
	printf("更改后:%s\n", b);
	printf("测试情况3\n更改前:%s\n", c);
	my_memmove(c + 4, c, 10);  //src+n>dest>src
	printf("更改后:%s\n", c);
	return 0;
}

输出分析:

(1)对于情况1的测试 

my_memmove(a + 9,a + 4,5);  //dest>=src+n

从a[4]起始的5个字节的内容("56789")复制到a[9]起始的5个字节的空间(“01234”)。此时dest>=src+n,从低地址往高地址正常复制即可,复制完应为:“123456789(a[0]到a[8]不动)56789(a[9]到a[13]为a[4]到a[8]粘贴的内容)567890(a[14]到a[19]不动)”,故结果为:"12345678956789567890"

(2)对于情况2的测试 

my_memmove(b, b+5, 5); //dest<=src

从b[5]起始的5个字节的内容("fghij")复制到b[0]起始的5个字节空间("abcde")。此时dest<=src,从低地址往高地址正常复制即可,复制完应为:"fghij(被粘贴的内容)fghijklmnopqrst(后面的不变)",故结果为:"fghijfghijklmnopqrst"

(3)对于情况3的测试 

my_memmove(c+4, c, 10);  //src+n>dest>src

从c[0]起始10个字节的空间("abcdefghij")被复制到c[4]起始10个字节的空间("efghij1234"),此时src+n>dest>src,需要从高地址往低地址进行复制,复制完应为:"abcd(c[0]到c[3]不变) abcdefghij(被粘贴的内容)567890(c[14]到c[19]不变])"

程序运行结果:

3:memset

1. 原理

函数作用:将内存块设置为指定字符(初始化)

函数结构为:void *memset( void *dest, int c, size_t count );

函数参数介绍
参数介绍(源于MSDN)
dest目标内存空间的起始地址。参数类型: void *
c初始化字符的ASCII值(具体可查ASCII表,举例:'0'=48),参数类型:int
count初始化内存空间大小(单位:字节)。参数类型:unsigned int
返回值返回dest的值
函数功能:从目标空间起始地址开始,将往后的count个字节的内容设置为c对应的字符。

这里给出ASCII表:

ASCII表

2. 模拟代码

void* my_memset(void* dest, int c, uint count)
{
	char* des = dest;
	while (count --)
	{
		*des = c;
		des++;
	}
	return dest;
}

3. 测试与结果分析

测试代码:

int main()
{
	char a[11]="1234567890";
	printf("未初始化之前的字符串:%s\n",a);
	printf("前5个字符初始化为字符*,后5个字符初始化为字符0\n");
	my_memset(a, '*', 5);
	printf("初始化之后的字符串:%s\n", a);
	my_memset(a + 5, 48, 5);
	printf("初始化之后的字符串:%s\n", a);
	return 0;
}

这里要说的就一点:函数传参时,无论函数参数是int型还是char型,我们都可以传递字符或数字,函数会通过ASCII表自己进行匹配。

比如:测试代码中my_memset的第二个参数为int型,如果我们调用时传递字符(my_memset(a, '*', 5)),函数会自动将 '*' 转化为42。反之如果函数参数为char型,我们传递int型数字时,函数会自动将数字转化为对于的ASCII字符。

测试结果:

4:memcmp

1. 原理

函数作用:内存比较函数

函数结构为:int memcmp( const void *buf1, const void *buf2, size_t count );

函数参数
参数介绍(源于MSDN)
buf1一块内存空间的起始地址。参数类型:const void *
buf2另一块内存空间的起始地址。参数类型:const void *
count内存比较长度(单位:字节)。参数类型:unsigned int
返回值返回比较结果(*buf1-*buf2:相等返回0 不相等返回正数或负数)
函数功能:比较二块内存空间的大小

内存比较原理:

首先从buf1和buf2空间的起始地址开始比较,判断起始地址处的字符是否相等。如果相等,地址增加,继续判断二块空间的字符是否相等。如果不相等,返回此地址字符相减的结果(二个字符相减,相当于二个字符ASCII对应的数字相减,得到的数字作为int型数据,并将此数值返回)。

所以函数功能可概述为:判断二块内存空间的字符串是否相等,如果相等返回0。如果不相等,则返回第一个不相等地址处字符的差值。

2. 模拟代码

int my_memcmp(const void* buf1, const void* buf2, uint count)
{
	while (count--)
	{
		if (*(char*)buf1 == *(char*)buf2)
		{
			buf1=(char*)buf1 + 1;
			buf2 = (char*)buf2 + 1;
		}
		else
		{
			return (int)(*(char*)buf1 - *(char*)buf2);
		}
	}
	return 0; 
}

3. 测试与结果分析

测试代码:

int main()
{
	//测试1 相等字符串
	char a[] = "abcde";
	char b[] = "abcde";
	if (my_memcmp(a, b, 5) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_memcmp(a, b, 5));
	}
	//测试2 不相等字符串
	char c[] = "abcdefg";
	char d[] = "abcde!";
	if (my_memcmp(c, d, 6) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_memcmp(c, d, 6));
	}
	return 0;
}

结果分析:

测试1:测试二个相等字符串,这没什么好说的。输出肯定为:二个字符串相等、

测试2:二个字符串的第6个字符不相同,c[5]='f'对应ASCII数字为102,d[5]='!'对应ASCII数字为33,。那么返回值应为:102-33=69。

程序运行结果:

3、字符串操作函数

字符串操作主要有:复制、连接、和比较。

1:strcpy和strncpy

1. 原理

函数作用:将一个字符串复制到另一个字符串之中。

函数结构:

char *strcpy( char *strDestination, const char *strSource );

char *strncpy( char *strDest, const char *strSource, size_t count );

二个函数都是将strSource起始位置的字符串复制到strDestination起始的空间中。

二者区别:

(1)strcpy复制时,如果*strSource =='\0',则停止复制(把最后的'\0'也复制过去),否则strSource ++。最后返回strDestination。

(2)strncpy复制是,可以指定复制的长度。但如果strncpy至strncpy+count空间内存在'\0',则'\0'之后的字符都被替换为'\0'。最后返回strDest。

(3)与内存复制函数不同,在使用strcpy和strncpy时,必须保证二块空间没有重叠,否则会出现意外的错误。

2. strcpy模拟代码和测试

模拟代码:

char* my_strcpy(char *arr2,const char *arr1)
{
	char* s = arr2;
	while (*( s ++) = *( arr1 ++));
	return arr2;
}

测试程序:

int main()
{
	char arr1[200] = { '0' };
	char arr2[200] = { '0' };
	int i = 0;
	int j = 0;
	printf("请输入一个字符串\n");
	while (scanf("%c", &arr1[i]) && arr1[i] != '\n')//将键入的字符串存放到arr中
	{
		i++;
	}
	printf("共键入%d个字符\n",i);
	printf("粘贴前,arr2的前%d个字符为:\n", i);
	for (j = 0; j <= i; j++)
	{
		printf("%c", arr2[j]);
	}
	printf("\n粘贴后,arr2的前%d个字符为:\n", i);
	my_strcpy(arr2,arr1);
	for (j = 0; j <= i; j++)
	{
		printf("%c",arr2[j]);
	}
}

程序运行结果:

3.strncpy模拟代码和测试

模拟代码:

char* my_strncpy(char* str1, const char* str2, uint num)
{
	char* s = str1;
	while (num--)
	{
		if (*str2 != '\0')
		{
			*s = *str2;
			s++;
			str2++;
		}
		else //num>str2的长度,直接终止
		{
			break;
		}
	}
	return str1;
}

参数程序:

int main()
{
	char a[20]= "abcdefghiss";
	printf("原字符串为:%s\n要粘贴的字符串为:123\n 粘贴长度为4,粘贴起始位置为f\n粘贴完成后的字符串为:%s",a, my_strncpy(a+5,"123",4)-5);
	return 0;
}

程序运行结果:

2: strcat和strncat

1. 原理

函数作用:将一个字符串粘贴到另外一个字符串后面。

函数结构:

char *strcat( char *strDestination, const char *strSource );

char *strncat( char *strDest, const char *strSource, size_t count );

二个函数都是将strSource起始位置的字符串粘贴到strDestination起始空间的后面。

二者区别:

(1)无论是strcat还是strncat,都要求strDestination起始的字符串结尾有'\0'。而strcat还要求strSource 的结尾也有'\0',否则就停不下来了。

(2)使用strcat时,必须要保证strDestination的空间够大,必须能放得下strSource 起始的字符串

(3)无论是strcat都不能用来粘贴自身,因为这样会把字符串结尾的'\0'给覆盖度,导致意外的现象。而strncat可以。

2. strcat模拟代码和测试

模拟代码:

char* my_strcat(char* strdest, const char* strsource)
{
	char* s = strdest;
	while (*s != '\0')
	{
		s++;
	}
	while (*strsource != '\0')
	 {
		*s = *strsource;
		s++;
		strsource++;
	 }
	return strdest;
}

参数程序:

int main()
{
	char a[20] = "abcdefg..!";
	char b[] = "zah scabv";
	printf("原字符串为:%s\n%s\n粘贴长度为:8\n粘贴后的字符串为:%s", a, b, my_strcat(a, b));
	return 0;
}

程序运行结果:

3.strncat模拟代码和测试

模拟代码:

char* my_strncat(char * strdest, const char *strsource, uint count)
{
	char * s= strdest;
	while (*s != '\0')
	{
		s++;
	}
	while (count--)
	{
		if (*strsource != '\0')
		{
			*s = *strsource;
			s++;
			strsource++;
		}
		else  //count>strsource的长度   直接终止
		{
			break;
		}
	}
	return strdest;
}

参数程序:

int main()
{
	char a[20] = "abcdefg..!";
	char b[] = "zah scabv";
	printf("原字符串为:%s\n%s\n粘贴长度为:8\n粘贴后的字符串为:%s",a,b, my_strncat(a,b,8));
	return 0;
}

程序运行结果:

3:strcmp和strncmp

1. 原理

函数作用:比较字符串

函数结构:

int strcmp( const char *string1, const char *string2 );

int strncmp( const char *string1, const char *string2, size_t count );

二个函数都是将从左往右比,比的是字符对应ASCII的大小。碰见第一个不相同的字符则停止比较,返回二个字符ASCII对应数字的差,返回值数据类型为:int

返回值= (int)(*string1 - *string2 )

二者区别:strncmp可以规定比较范围,而strcmp会全部比完或碰见不同字符时停止。

2. strcmp模拟代码和测试

模拟代码:

int my_strcmp(const char* buf1, const char* buf2)
{
	while (*buf1++ == *buf2++)
	{
		if (*buf1 == '\0')  //字符串相等
			return 0;
	}
	return (int)(*buf1 - *buf2);
}

参数程序:

int main()
{
	//测试1 相等字符串
	char a[] = "abcde";
	char b[] = "abcde";
	if (my_strcmp(a, b, 5) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_strcmp(a, b));
	}
	//测试2 不相等字符串
	char c[] = "abcdefg";
	char d[] = "abcde!";
	if (my_strcmp(c, d) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_strcmp(c, d));
	}
	return 0;
}

程序运行结果:

3.strncmp模拟代码和测试

模拟代码:

int my_strncmp(const char* buf1, const char* buf2, uint count)
{
	while (count--)
	{
		if (*buf1 == *buf2)
		{
			buf1 ++;
			buf2 ++;
		}
		else
		{
			return (int)(*buf1 - *buf2);
		}
	}
	return 0;
}

参数程序:

int main()
{
	//测试1 相等字符串
	char a[] = "abcde";
	char b[] = "abcde";
	if (my_strncmp(a, b, 5) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_strncmp(a, b, 5));
	}
	//测试2 不相等字符串
	char c[] = "abcdefg";
	char d[] = "abcde!";
	if (my_strncmp(c, d, 6) == 0)
	{
		printf("二个字符串相等\n");
	}
	else
	{
		printf("二个字符串不相等,返回结果为:%d\n", my_strncmp(c, d, 6));
	}
	return 0;
}

程序运行结果:

5、结束语和一些相似函数的对比

1: 拷贝函数memcpy、memmove、strcpy和strncpy

memcpy和memmove都是将一块内存块的内容复制到另一块内存中。唯一的区别是:如果二个内存块的空间重叠,必须使用memmove。当二个内存块空间不重叠时,二个函数的功能没有区别。(就相当于,如果你想把一个桌子从一个房间搬到另外一个房间,memcpy和memmove都可以帮你实现这个效果。但如果你只是想把桌子往旁边挪一挪,那么就必须使用memmove)。而strcpy和strncpy是字符串操作函数,理论为strcpy和strncpy能干的事情,memcpy和memmove都能干,反之不一定。

区别1:memcpy、memmove和strncpy都需要输入拷贝字符的数量,而strcpy则不需要,其碰见字符串中的'\0'自动停止,所以在使用字符数组时,一定要确定字符串最后有个'\0'。

区别2:strcpy和strncpy使用时一定要注意二个字符串空间不能重叠,否则会出现意外。

区别3:strncpy在复制时碰见'\0',后面都会复制为'\0'。

2: 比较函数memcmp、strcmp和strncmp

strcmp和strncmp是比较字符串是否相同,唯一的区别的strncmp可以控制比较范围。

memcmp是内存比较函数,与字符比较函数的区别是:strcmp和strncmp在比较时,需要注意字符串是否结束,即使是strncmp也不会比较'\0'后面的字符。而memcmp不管那么多,哪怕是'\0',也会进行比较。

3:结束语

文章较长,这是因为知识点太多。还有很多文中没有涉及的字符串函数,后期本人会继续对本文进行完善。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值