字符串函数和内存函数如何使用,以及如何模拟实现(详解)

文章目录


在这里插入图片描述

字符函数+字符串函数+内存函数

❤️1.strlen函数

💕1.1strlen函数介绍及使用

👻strlen函数返回的是‘\0’以前出现的字符个数,遇到’\0’就不数了,就如同你遇到你终身最合适伴侣,你就不再找了,你就会和他/她一起好好过日子。注意strlen函数的返回值是size_t,是无符号的。

👻strlen函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	int num = strlen(arr);   //调用strlen函数
	printf("num=%d\n", num);
	return 0;
}

strlen函数使用的结果图

💕1.2strlen与sizeof的区别

🐷strlen函数求的是字符串在‘\0’以前的字符个数,不包括‘\0’;
🐗sizeof求的是字符串在内存中所占的长度,包括‘\0’;
😍还有注意sizeof只是运算符,并不是函数。

🤡下面用图片和代码让你们瞧瞧它们之间的差别
在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	int num = strlen(arr);
	int size = sizeof(arr);
	printf("num=%d\n", num);
	printf("size=%d\n", size);
	return 0;
}

在这里插入图片描述

💕1.3strlen函数的一个小细节

🤡问题: 定义一个字符数组,char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };int num=strlen(arr);请问num等于多少?
🐒我想大部分初学者一定会说num等于6吧,那么事实是这样的吗?来,我们来瞅瞅代码。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	int num = strlen(arr);
	printf("num=%d\n", num);
	return 0;
}

在这里插入图片描述

奇怪了,为何在计算机的编译器上实现,num却等于12呢?
来,听我慢慢道来。
因为当我们定义一个字符串,需要用到字符数组,如果我们需要将一个字符串abcdef存储到字符数组arr中;我们有两种方式初始化:
👻①char arr[]=“abcef”;这样初始化,abcdef存储到字符数组之后,计算机会在abcdef之后添加一个字符串结束字符’\0’;则int num=strlen(arr),这里求出来的num等于6;
👻②char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘\0’ };因为不是像第一种方式以字符串的形式初始化,计算机不会在abcdef后面自动添加一个字符串结束字符’\0’;这样int num=strlen(arr),这里求出来的num才等于6;而如果像我刚才这样char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ }初始化,则int num=strlen(arr),所以这里求出来的num等于一个随机值,这个随机值肯定会大于正确结果值,至于随机值最后会等于多少,就看这个’字符串结束字符\0’出现在哪了,况且每一次重新运行这一部分代码,计算机会为字符串数组重新分配空间,则’\0’出现位置也在变化,所以num的值也可能会发生变化。

例如这两张图,代码相同,而num的值却不同。在这里插入图片描述在这里插入图片描述

图片解释
🤡strlen函数越界访问了在这里插入图片描述

💕1.4如何模拟实现strlen函数

【算法步骤】

①用一个指针str接收数组传参,定义一个计数器num=0
②字符不为’\0’,str++,num++

【伪代码】

size_t my_strlen(const char* str)
{
	int num = 0;
	while (*str != '\0')
	{
		num++;
		str++;
	}
	return num;
}

【算法分析】

当初始化正确时,字符串的个数就是while语句执行的次数,则时间复杂度为O(n);当初始化不正确时,我也不能确定它的时间复杂度是多少,如有大佬知道,请在评论区告诉我,谢谢。

【完整代码】

#include <stdio.h>
size_t my_strlen(const char* str)
{
	int num = 0;
	while (*str != '\0')
	{
		num++;
		str++;
	}
	return num;
}
int main()
{
		char arr[] = "abcdef";
	int num = my_strlen(arr);
	printf("num=%d\n", num);
	return 0;
}

💚2.strcpy函数和strncpy函数

💕2.1strcpy函数和strncpy函数的介绍及使用

👻 strcpy函数将源字符串拷贝到目标空间,注意源字符串必须以’\0’结尾,会将源字符串中的’\0’拷贝到目标空间,注意目标空间必须足够大,以确保能够存放源字符串,注意目标空间必须可变。
假如源字符串不以’\0’结尾,将源字符串拷贝到目标空间时,strcpy函数会进行越界访问。
假如目标空间不够大,则strcpy函数将源字符串进行拷贝到目标空间时,程序会崩溃。
假如目标空间不可变,例如char *arr1=“abcdefrt”; char arr2[]=“hyt”;将arr2拷贝到arr1中,使用strcpy(arr1,arr2)时,你会发现程序会崩溃,因为arr1指向的是常量字符串。

👻 strncpy函数将n个源字符串拷贝到目标空间,n的大小我们自己设定;如果源字符串的长度小于n,则拷贝完源字符串,后面补’\0’,直到n个。注意源字符串必须以’\0’结尾,会将源字符串中的’\0’拷贝到目标空间,注意目标空间必须足够大,以确保能够存放源字符串,注意目标空间必须可变。

👻strcpy和strncpy函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
		char arr1[] = "zhangsan";
		char arr2[] = "lisi";
		strcpy(arr1, arr2);  //调用strcpy函数
	    printf("%s\n", arr1);
	    return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "zhangsan";
	char arr2[] = "lisi";
	strncpy(arr1, arr2, 3); //调用strncpy函数
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述

💕 2.2strcpy与strncpy的区别

想必当你看完这两个函数的介绍之后,它们有何区别,我觉得你心里应该也有点谱了吧!
☃️strcpy只能将源字符串全部拷贝到目标空间,不可控;
strncpy可以按照自己的要求来需要从源字符串拷贝几个字符到目标空间就拷贝几个,可控。

💕 2.3如何模拟实现strcpy和strncpy函数

💞strcpy的模拟实现

【算法步骤】

①利用两个指针destination和source接收两个数组传参;
②指针source将源字符串里面的字符一个个赋值给指针destination,直到指针source指向’\0’,将’\0’赋给destination,拷贝结束;
③返回目标空间的首地址。

【伪代码】

char* my_strcpy(char* destination, const char* source)
{
	char* ret = destination;
	while (*destination++ = *source++)
	{

	}
	return ret;
}

【算法分析】

前提条件,源字符串必须以’\0’结尾,字符串的个数就是while语句执行的次数,则时间复杂度为O(n);

【完整代码】

#include <stdio.h>
char* my_strcpy(char* destination, const char* source)
{
	char* ret = destination;
	while (*destination++ = *source++)
	{

	}
	return ret;
}

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

💞strncpy的模拟实现

【算法步骤】

①利用两个指针destination和source接收两个数组传参,num记录需要拷贝几个字符
②需要从源字符串中拷贝num个字符
③第一种情况num小于等于源字符串的字符个数,num为0,拷贝成功
④第二种情况num大于源字符串的字符个数,将源字符串全部传过去后,后面补’\0’,直到num为0
⑤返回起始地址

【伪代码】

char* my_strncpy(char* destination, const char* source, size_t num)
{
	assert(destination && source);      //断言,防止destination 和 source为空指针
	char* ret = destination;        //记录首地址
	while (num && (*destination++ = *source++) != '\0')
	{
		num--;
	}
	while (num != 0)   //拷贝的个数超出源字符串的字符个数
	{
		*destination++ = '\0';
		num--;
	}
	return ret;
}

【算法分析】

while的执行次数与num有关,所以时间复杂度为O(n)

【完整代码】

#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strncpy(char* destination, const char* source, size_t num)
{
	assert(destination && source);      //断言,防止destination 和 source为空指针
	char* ret = destination;
	while (num && (*destination++ = *source++) != '\0')
	{
		num--;
	}
	while (num != 0)   //拷贝的个数超出源字符串的字符个数
	{
		*destination++ = '\0';
		num--;
	}
	return ret;
}
int main()
{
	char arr1[] = "zhangsan";
	char arr2[] = "lisi";
	//strncpy(arr1, arr2,4);
	my_strncpy(arr1, arr2, 8);
	printf("%s\n", arr1);
	return 0;
}

❤️3.strcat函数和strncat函数

💕3.1strcat函数和strncat函数的介绍及使用

👻strcat函数的作用是将源字符串连接到目标字符串后面。注意源字符串必须以’\0’结尾;注意目标字符串的空间必须足够大,足以存放源字符串;注意目标空间必须可修改。

👻strncat函数将源字符串中的n个字符连接到目标字符后面。注意源字符串必须以’\0’结尾;注意目标字符串的空间必须足够大,足以存放源字符串;注意目标空间必须可修改。

👻strcpy和strncpy函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "hello";
	char arr2[] = "world";
	strcat(arr1, arr2);   //目标字符串arr1,源字符串arr2
	printf("%s", arr1);
	return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "hello";
	char arr2[] = "world";
	strncat(arr1, arr2, 2);  //arr1目标字符串,arr2源字符串,2为需要连接的字符个数
	printf("%s", arr1);
	return 0;
}

在这里插入图片描述

💕 3.2strcat与strncat的区别

看了它们的介绍,想必你也能猜出它们的区别了吧!strncat可以控制源字符串的连接个数,而strcat却不行。

💕3.3如何模拟实现strcat函数和strncat函数

💞strcat函数的模拟实现

【算法步骤】

①先用两个字符指针dst和src分别接收目标字符串的首地址和源字符串的首地址;
②先将指针dst指向目标字符串的末尾’\0’处;
③将src指向的源字符串连到dst指向的目标字符串后面,即指针src将源字符串里面的字符一个个赋给指针dst指向的目标字符串,从目标字符串的’\0’开始赋值,直到将源字符串中的字符’\0’赋值到目标字符串中。
④返回目标字符串的首地址

【伪代码】

char* my_strcat(char* dst, const char* src)
{
	char* ret = dst;
	while (*dst)        //指针指到目标字符串的'\0'处
	{
		dst++;
	}
	while (*dst++ = *src++)  //开始连接
	{
		;
	}
	return ret;
}

【算法分析】

while语句执行次数与目标字符串的字符个数n和源字符串的字符个数m有关,所以时间复杂度为O(n+m)

【完整代码】

#include <stdio.h>
char* my_strcat(char* dst, const char* src)
{
	char* ret = dst;
	while (*dst)        //指针指到目标字符串的'\0'处
	{
		dst++;
	}
	while (*dst++ = *src++)  //开始连接
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = "world";
	my_strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

💞 strncat函数的模拟实现

【算法步骤】

①先用两个字符指针dst和src分别接收目标字符串的首地址和源字符串的首地址,num接收需要连接的个数
②先将指针dst指向目标字符串的末尾’\0’处;
③将源字符串中的num个字符连接到目标字符串后面,根据num的大小有两种情况:
⛄第一种,当num小于源字符串的字符总数时,将num个字符连到目标字符串后面之后,再赋值一个字符’\0’过去,然后连接结束。
☃️第二种,当num大于等于源字符串的字符总数时,当将源字符串中的字符’\0’赋值到目标字符串之后,连接结束。

【伪代码】

char* my_strncat(char* dst, const char* src, size_t num)
{
	char* ret = dst;
	while (*dst)    //指针指到目标字符串的'\0'处
	{
		dst++;
	}
	while (num--)
	{
		if((*dst++ = *src++)==0)     //num大于等于源字符串的字符总数时
			return ret;
	}
	*dst = '\0';               //num小于源字符串的字符总数,在目标字符串最后赋一个'\0'
	return ret;
}

【算法分析】

①while的执行次数与目标字符串的字符总数n和(前提条件num小于等于源字符串的字符总数)需要连接的个数num有关,时间复杂度为O(n+num)
②while语句执行次数与目标字符串的字符个数n和(前提条件num大于源字符串的字符总数)源字符串的字符个数m有关,所以时间复杂度为O(n+m)

【完整代码】

#include <stdio.h>
char* my_strncat(char* dst, const char* src, size_t num)
{
	char* ret = dst;
	while (*dst)
	{
		dst++;
	}
	while (num--)
	{
		if((*dst++ = *src++)==0)
			return ret;
	}
	*dst = '\0';
	return ret;
}

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

💚4.strcmp函数和strncmp函数

💕4.1strcmp函数和strncmp函数的介绍及使用

👻strcmp函数可以判断第一个字符串与第二个字符串大小,第一个字符串大于第二个字符串,则返回大于0的数字;第一个字符串等于第二个字符串,则返回0;第一个字符串小于第二个字符串,则返回小于0的数字。

👻strcmp函数可以控制比较两个字符串前num个字符,直到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

👻strcpy和strncpy函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcdf";
	int num=strcmp(arr1, arr2);  //使用strcmp函数
	printf("%d", num);
	return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcdf";
	int num=strncmp(arr1, arr2,3); //比较前面三个字符
	printf("%d", num);
	return 0;
}

在这里插入图片描述

💕4.2strcmp函数与strncmp函数的区别

☃️根据前面的介绍,就是一个不可控,一个却可控,我相信聪明的你肯定能明白!

💕 4.3如何模拟实现stcmp函数和strncmp函数

💞strcmp函数的模拟实现

【算法步骤】

①用指针str1和str2分别接收字符串1和字符串2的首地址。
②用指针str1和str2将字符串1和字符串2里面的字符依次相互比较,直到两个字符出现不同,或者一个字符串结束。

【伪代码】

int my_strcmp(const char* str1, const char* str2)
{
	while ((*str1++ != '\0') && (*str2++ != '\0'))
	{
		if (*str1 > *str2)
			return 1;
		if (*str1 < *str2)
			return -1;
    }
	return 0;
}

【算法分析】

while语句执行的次数,最好的情况一次,最坏的情况一个字符串(n)比较完,所以时间复杂度为O(n)。

【完整代码】

#include <stdio.h>
int my_strcmp(const char* str1, const char* str2)
{
     assert(str1 && str2);       //防止空指针
	while ((*str1 != '\0') && (*str2 != '\0'))
	{
		if (*str1 > *str2)
			return 1;
		if (*str1 < *str2)
			return -1;
			str1++;
			str2++;
    }
	return 0;
}
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcdf";
	int num = my_strcmp(arr1, arr2);
	printf("%d", num);
	return 0;
}

💞strncmp函数的模拟实现

【算法步骤】

①用指针str1和str2分别接收字符串1和字符串2的首地址。
②用指针str1和str2将字符串1和字符串2的前num个字符依次相互比较,直到两个字符出现不同,或者一个字符串结束。

【伪代码】

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);       //防止空指针
	while (num )
	{
		if (*str1 > *str2)
			return 1;
		if (*str1 < *str2)
			return -1;
                str1++;
                str2++;
				num--;
	}
	return 0;
}

【算法分析】

while语句的执行次数与num有关,所以时间复杂度为O(n)

【完整代码】

#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);       //防止空指针
	while (num )
	{
		if (*str1 > *str2)
			return 1;
		if (*str1 < *str2)
			return -1;
                str1++;
                str2++;
				num--;
	}
	return 0;
}


int main()
{
	char arr1[] = "abc\0de";
	char arr2[] = "abcdf";
	int num = my_strncmp(arr1,arr2,4);
		printf("%d", num);
	return 0;
}

❤️5.strstr函数

💕5.1strstr函数的介绍及使用

👻strstr函数的作用,找子串,给定一个源字符串,再给一个子串,strtstr函数可以判断出源字符串中是否存在这个子串。如果源字符串存在子串,返回源字符串中子串的起始地址,如若不存在,返回空指针。

👻strstr函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdeabcdef";
	char arr2[] = "cde";
	char* p = strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("不存在\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

💕5.2如何模拟实现strstr函数

【算法步骤】

①用两个指针str1和str2分别接收源字符串的地址和子串的地址
②再定义两个移动指针s1和s2,让它们先分别接收源字符串和子串的初始地址;在定义一个指针p,指针p的作用是当移动指针s1和s2从源字符串中找子串时,会出现指针s1和s2所指字符比对不成功的情况,这时s2回到子串初始地址,而s1回到源字符串初始位置加一的位置(p++),依次逻辑反复下去,直到找到子串,或者指针p指到了’\0’。

🤡大致的逻辑图解
在这里插入图片描述

【伪代码】

char* my_strstr(char* str1, const char* str2)
{
	assert(str1 && str2);         //判断是否为空指针
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p != '\0')
	{
		s1 = p;           
		s2 = str2;           //s2重新回到子串初始位置
		while ((s1!='\0'&&s2!='\0')&& (* s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;    //找到了
		p++;            //从源字符串中的下一个位置开始
	}
	return NULL;         //未找到
}

【算法分析】

①最好情况:假设源字符串为abcdeabcdef(字符个数为n),子串为abc(字符个数为m),时间复杂都为O(m)
②最坏的情况:假设源字符串为abfabfabfabfabfabf(字符个数为n),子串为abq(字符个数为m),时间复杂度为O(nm)
③平均复杂度为O(n
m)

【完整代码】

#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strstr(char* str1, const char* str2)
{
	assert(str1 && str2);         //判断是否为空指针
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p != '\0')
	{
		s1 = p;           
		s2 = str2;           //s2重新回到子串初始位置
		while ((s1!='\0'&&s2!='\0')&& (* s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;    //找到了
		p++;            //从源字符串中的下一个位置开始
	}
	return NULL;         //未找到
}
int main()
{
	char arr1[] = "abcdeabcdef";
	char arr2[] = "cde";
	char* p = strstr(arr1, arr2);
	//char* p = my_strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("不存在\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

💚6.strtok函数

💕6.1strtok函数的介绍及使用

先看strtok函数的自定义吧

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

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

👻strtok函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

该代码的目的是,想得到字符串“zpengwei@bitedu.com”里面的zpengwei、bitedu、com这些子串

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "zpengwei@bitedu.com";       //举例字符
	char buf[200] = { 0 };
	strcpy(buf, arr);     //临时拷贝
	char* p = "@.";       //节点集合
	char *str = strtok(buf, p);  //str接收返回地址,strtok函数将第一个节点@置为\0
	printf("%s\n", str);
	//继续,想要取字符串第一个节点后面的部分
	//strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
	str = strtok(NULL, p);
	printf("%s\n", str);
	//strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
	str = strtok(NULL, p);
	printf("%s\n", str);
	return 0;
}

//进一步修改
//如何更优雅利用strtok函数
int main()
{
		char arr[] = "zpengwei@bitedu.com";       //举例字符
	    char buf[200] = { 0 };
	    strcpy(buf, arr);     //临时拷贝
	    char* p = "@.";       //节点集合
		char* str=NULL;
		for (str = strtok(buf, p);str != NULL;str=strtok(NULL,p))
		{
			printf("%s\n", str);
		}
	return 0;
}

💕 6.2如何模拟实现strtok函数

【插入一个小知识点,static的作用】

🤡c语言static意思是静态变量。static指修饰符,可以用来修饰变量,也可以用来修饰函数。如果是在函数外面定义的,那么static说明的变量可以在当前c程序文件中使用。如果是在函数内部定义的,那么这个变量只初始化一次,即使再次调用这个函数,这个static变量也不会再次被初始化。

【算法步骤】

🤡使用该函数的目的是能够将所有节点赋值为’\0’,并返回子串的起始地址。
①使用两个指针,一个指针str接收源字符串的首地址或者是空指针,另一个指针sep接收节点集合的首地址。
②定义指针ret记录子串的首地址;定义一个静态指针p初始化为空;定义一个num对分隔符做标记,其实就是计数;
③定义一个整型flag变量来记录节点集合的字符个数,如果flag=0,返回源字符串的初始地址;不为0再进行后续操作。
④如果str不是空指针,是源字符串的起始地址,则指针str开始从源字符串的起始地址开始往后挪,将所指的字符依次与节点集合的第一个节点字符开始比较,找到第一个节点字符,将其赋值为’\0’,即*str=‘\0’;指针p记录第二个子串的位置p=str+1;num赋值为0;
⑤如果str是空指针,则已经开始查找至少是同一个源字符串第二个的子串了,后续流程跟第④差不多,我就不再详细描述了。
⑥如果表计数超过了分割符总数,则分割已结束返回空指针。if (num > flag) return NULL;

【伪代码】

char* my_strtok(char* str, const char* sep)
{
	 static int n = 0;    //n为求源字符串的字符总数的计数器
	 static num = 0;     //用来记录前面一个子串的大小,包括节点集合的字符
	 int m = 0;         //m为求节点集合的字符总数的计数器
	 int k = 0;        //判断这个字符是否属于节点集合里面的字符
	 char* ret = str;
	 static char* p = NULL;        //p用来记录非第一个子串的初始地址
	 int i = 0;
	 int j = 0;
	 for (i = 0;sep[i] != '\0';i++)  //求节点集合的字符总数
	 {
		 m++;
	 }
	 if (m == 0)
		 return ret;
	 if (str != NULL)
	 {
		   n = 0;
		 for (i = 0;str[i] != '\0';i++)  //求源字符串的字符总数
		 {
			 n++;
		 }
		 for (j = 0;j < n;j++)
		 {
			   k = 0;
			 for (i = 0;i < m;i++)
			 {
				 if (*ret != sep[i])  //判断子串初始地址所存储的字符是否为节点集合里面的字符
					 k++;
			 }
			 for (i = 0;i < m;i++)
			 {
				 if ((str[j] == sep[i]) && (k !=m))  //如果子串初始值节点集合里面的字符,ret++
				 {
					 ret++;
				 }
				 if ((str[j] == sep[i]) && (k == m)) //如果子串初始值非节点集合里面的字符,将末尾节点字符赋值为'\0'
				 {
					 str[j] = '\0';
				 }
			 }
			 if (str[j] == '\0')
			 {
				 p = str + j + 1;  //将指针指向下一个子串
				 num = j + 1;     //记录前面子串的大小,包括节点字符
				 return ret;
			 }
		 }

	 }
	 else if (str == NULL)  //此部分代码与上面代码的逻辑类似,只是稍做了修改
	 {
		 ret = p;
		 n = n - num;
		 for (j = 0;j <= n;j++)
		 {
			 k = 0;
			 for (i = 0;i < m;i++)
			 {
				 if (*ret != sep[i])
					 k++;
			 }
			 for (i = 0;i < m;i++)
			 {
				 if ((p[j] == sep[i]) && (k != m))
				 {
					 ret++;
				 }
				 if ((p[j] == sep[i]) && (k == m))
				 {
					 p[j] = '\0';
				 }
			 }
			 if (p[j] == '\0'&&(j!=n))
			 {
				 p = p + j + 1;
				 num = j + 1;
				 return ret;
			 }
			 if(p[j] == '\0' && (j == n)) //当指针指向末尾时
			 {
				 return ret;
			 }
		 }
	 }

}

【算法分析】

该模块代码的执行时间主要与while语句执行次数有关,而while的执行次数主要与源字符串的字符个数有关,所以时间复杂度为O(n)。

【完整代码】

#inlclude <stdio.h>
char* my_strtok(char* str, const char* sep)
{
	 static int n = 0;    //n为求源字符串的字符总数的计数器
	 static num = 0;     //用来记录前面一个子串的大小,包括节点集合的字符
	 int m = 0;         //m为求节点集合的字符总数的计数器
	 int k = 0;        //判断这个字符是否属于节点集合里面的字符
	 char* ret = str;
	 static char* p = NULL;        //p用来记录非第一个子串的初始地址
	 int i = 0;
	 int j = 0;
	 for (i = 0;sep[i] != '\0';i++)  //求节点集合的字符总数
	 {
		 m++;
	 }
	 if (m == 0)
		 return ret;
	 if (str != NULL)
	 {
		   n = 0;
		 for (i = 0;str[i] != '\0';i++)  //求源字符串的字符总数
		 {
			 n++;
		 }
		 for (j = 0;j < n;j++)
		 {
			   k = 0;
			 for (i = 0;i < m;i++)
			 {
				 if (*ret != sep[i])  //判断子串初始地址所存储的字符是否为节点集合里面的字符
					 k++;
			 }
			 for (i = 0;i < m;i++)
			 {
				 if ((str[j] == sep[i]) && (k !=m))  //如果子串初始值节点集合里面的字符,ret++
				 {
					 ret++;
				 }
				 if ((str[j] == sep[i]) && (k == m)) //如果子串初始值非节点集合里面的字符,将末尾节点字符赋值为'\0'
				 {
					 str[j] = '\0';
				 }
			 }
			 if (str[j] == '\0')
			 {
				 p = str + j + 1;  //将指针指向下一个子串
				 num = j + 1;     //记录前面子串的大小,包括节点字符
				 return ret;
			 }
		 }

	 }
	 else if (str == NULL)  //此部分代码与上面代码的逻辑类似,只是稍做了修改
	 {
		 ret = p;
		 n = n - num;
		 for (j = 0;j <= n;j++)
		 {
			 k = 0;
			 for (i = 0;i < m;i++)
			 {
				 if (*ret != sep[i])
					 k++;
			 }
			 for (i = 0;i < m;i++)
			 {
				 if ((p[j] == sep[i]) && (k != m))
				 {
					 ret++;
				 }
				 if ((p[j] == sep[i]) && (k == m))
				 {
					 p[j] = '\0';
				 }
			 }
			 if (p[j] == '\0'&&(j!=n))
			 {
				 p = p + j + 1;
				 num = j + 1;
				 return ret;
			 }
			 if(p[j] == '\0' && (j == n)) //当指针指向末尾时
			 {
				 return ret;
			 }
		 }
	 }

}

//如何更优雅利用strtok函数
int main()
{
	char arr[] = "@@..zpengwei@.bit@..@..edu.com";       //举例字符
	char buf[200] = { 0 };
	strcpy(buf, arr);     //临时拷贝
	char* p = "@.";       //节点集合
	char* str = NULL;
	for (str = my_strtok(buf, p);str != NULL;str = my_strtok(NULL, p))
	{
		printf("%s\n", str);
	}
	return 0;
}

在这里插入图片描述

❤️7.strerror函数

💕7.1strerror函数的介绍及使用

👻返回错误码,所对应的错误信息。

👻函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
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));
	FILE* pf = fopen("test.txt", "r");
	//errno存储错误码
	if (pf == NULL)
	{
		perror("fopen");  //perror-->printf+strerror //依然是打印变量error中错误码的错误信息
		//printf("%s\n", strerror(errno));
		return 1;
	}
	//读文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

💚8.memcpy函数

💕8.1memcpy函数的介绍及使用

👻函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
注意所以memcpy可以适用任何数据类型的拷贝,不只局限于字符拷贝。

👻函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <sring.h>
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr1[20] = { 0 };
    memcpy(arr1, arr, 20);   //20表示20个字节并不是20个元素
    int i = 0;
    for (i = 0;i < 5;i++)
    {
        printf("%d ", arr1[i]);
    }
    return 0;
}

在这里插入图片描述

💕8.2如何模拟实现memcpy函数

【算法步骤】

先看memcpy函数的自定义吧

void * memcpy ( void * destination,     //目标空间
               const void * source,     //源数据   
               size_t num );            //拷贝多少字节,一个整型数据占4个字节

①先用两个void类型指针分别接收目标空间地址和源数据的起始地址,在用num接收字节总数。
②将void类型指针转化为char类型指针,然后将源数据前num个字节数据赋值给目标空间,直到num为0

【伪代码】

void* my_memcpy(void* destination, const void* source, size_t num)
{
    void* ret = destination;
    while (num)
    {
        *(char*)destination = *(char*)source;
        destination = (char*)destination + 1;
        source = (char*)source + 1;
        num--;
    }
    return ret;      //返回目标空间起始地址
}

【算法分析】

显然,时间复杂度为O(n)

【完整代码】

#include <stdio.h>
void* my_memcpy(void* destination, const void* source, size_t num)
{
    void* ret = destination;
    while (num)
    {
        *(char*)destination = *(char*)source;
        destination = (char*)destination + 1;
        source = (char*)source + 1;
        num--;
    }
    return ret;      //返回目标空间起始地址
}
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr1[20] = { 0 };
    /*float arr[] = { 1.0f,2.0f,3.0f,4.0f,5.0f,7.0f,8.0f,9.0f,10.0f };
    float arr1[20] = { 0.0 };*/
    memcpy(arr1, arr, 20);
    int i = 0;
    for (i = 0;i < 5;i++)
    {
        printf("%d ", arr1[i]);
    }
    return 0;
}

❤️9.memmove函数

💕9.1memmove函数的介绍及使用

👻函数memove从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

👻memove函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	memmove(arr1+2, arr1, 20);
	for (i = 0;i < 10;i++)
	{
		printf("%d ", arr1[i]);
	}
}

在这里插入图片描述

💕9.2如何模拟实现memmove函数

【算法步骤】

①先用两个void类型指针分别接收目标空间地址和源数据的起始地址,在用num接收字节总数。
②将源数据赋给目标空间,要考虑两种情况:
第一种情况:当目标空间的起始地址小于源数据地址时
☃️将void类型指针转化为char类型指针,然后将源数据前num个字节数据赋值给目标空间,数据的赋值顺序是从前开始赋值,也就是从第一个字节开始,直到num为0
第二种情况:当目标空间的起始地址小于源数据地址时
将void类型指针转化为char类型指针,然后将源数据前num个字节数据赋值给目标空间,数据的赋值顺序是从后开始赋值,也就是从最后一个字节开始,直到num为0

【伪代码】

void* my_memmove(void* destination, const void* source, size_t num)
{
	void* ret = destination;
		if (destination < source)   //目标地址小于源地址
		{
			        while (num)
				    {
				        *(char*)destination = *(char*)source;
				        destination = (char*)destination + 1;
				        source = (char*)source + 1;
				        num--;
				    }
		}
		else if (destination > source)
		{
			while (num--)
			{
				*((char*)destination + num) = *((char*)source + num);
			}
		}
		return ret;
}

【算法分析】

显然时间复杂度为O(n)

【完整代码】

#include <stdio.h>
void* my_memmove(void* destination, const void* source, size_t num)
{
	void* ret = destination;
		if (destination < source)   //目标地址小于源地址
		{
			        while (num)
				    {
				        *(char*)destination = *(char*)source;
				        destination = (char*)destination + 1;
				        source = (char*)source + 1;
				        num--;
				    }
		}
		else if (destination > source)    //目标地址大于源地址
		{
			while (num--)
			{
				*((char*)destination + num) = *((char*)source + num);
			}
		}
		return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	my_memmove(arr1+2, arr1, 20);
	for (i = 0;i < 10;i++)
	{
		printf("%d ", arr1[i]);
	}
}

💚10.memcmp函数

💕10.1memcmp函数的介绍及使用

👻memcmp先看memcmp函数的自定义吧

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

👻memcmp比较从ptr1和ptr2指针开始的num个字节。也就是比较两个数据前num个字节的大小。第一个数据比第二个数据大返回1,第一个数据比第二个数据小返回-1,第一个数据和第二个数据相等返回0。

👻memcmp函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{ 
    int arr1[] = { 1,2,3,4,5,6 }; //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00
    int arr2[] = { 1,2,3,5,0,0 }; //01 00 00 00 02 00 00 00 03 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00
    int ret = memcmp(arr1, arr2, 13);
    printf("%d\n", ret);
    return 0;
}

在这里插入图片描述

💕10.2如何模拟实现memcmp函数

【算法步骤】

①用两个指针分别接收两个数据的首地址,用num接收需要比较多少个字节数。
②比较从ptr1和ptr2指针开始的num个字节,第一个字节比第二个字节大返回1,第一个字节比第二个字节小返回-1,第一个字节和第二个字节相等返回0。

【伪代码】

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
    while (num)
    {
        if (*(char*)ptr1 > *(char*)ptr2)
            return 1;
        if (*(char*)ptr1 < *(char*)ptr2)
            return -1;
        ptr1 = (char*)ptr1 + 1;
        ptr2 = (char*)ptr2 + 1;
        num--;
    }
    return 0;
}

【算法分析】

①最好的情况:例如arr1[]={1},arr2[]={-1},num不论为何值,时间复杂度为O(1)
②最坏的情况:例如arr1[]={1,2,3,4,5},arr2[]={1,2,3,4,5},时间复杂度为O(num),即O(n)
③所以时间复杂度为O(n)

【完整代码】

#include <stdio.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
    while (num)
    {
        if (*(char*)ptr1 > *(char*)ptr2)  //第一个字节大于第二个字节
            return 1;
        if (*(char*)ptr1 < *(char*)ptr2)   //第一个字节小于第二个字节
            return -1;
        ptr1 = (char*)ptr1 + 1;
        ptr2 = (char*)ptr2 + 1;
        num--;
    }
    return 0;   //相等
}
int main()
{ 
    int arr1[] = { 1,2,3,4,5,6 }; //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00
    int arr2[] = { 1,2,3,5,0,0 }; //01 00 00 00 02 00 00 00 03 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00
     int ret= my_memcmp(arr1, arr2, 12);
    printf("%d\n", ret);
    return 0;
}

❤️11. memset函数

💕11.1memset函数的介绍及使用

👻memset函数的作用将前n个字节改成你想要的数字

👻memset函数的使用需要引用头文件 #include <string.h>,如何使用,请看下面的代码

#include <stdio.h>
#include <string.h>
int main()
{
    int arr[] = { 1,2,3,4,5 };
     memset(arr, 0, 8);    //将前八个字节改成0,一个整型数据八个字节
     int i = 0;
     for (i = 0;i < 5;i++)
     {
         printf("%d ", arr[i]);
     }
    return 0;
}

在这里插入图片描述

💕11.2如何模拟实现memset函数

【算法步骤】

利用一个指针接收数据首地址,用value接收想要插入的值,用num接收你想要插入多少个。

【伪代码】

void* my_memset(void* p1, int value, size_t num)
{
    int i = 0;
    for (i = 0;i < num;i++)
    {
        *((char*)p1 + i) = value;
    }
    return p1;
}

【算法分析】

显然时间复杂度为O(n)

【完整代码】

#include <stdio.h>
void* my_memset(void* p1, int value, size_t num)
{
    int i = 0;
    for (i = 0;i < num;i++)
    {
        *((char*)p1 + i) = value;
    }
    return p1;
}

int main()
{
    int arr[] = { 1,2,3,4,5 };
    my_memset(arr, 0, 8);
     int i = 0;
     for (i = 0;i < 5;i++)
     {
         printf("%d ", arr[i]);
     }
    return 0;
}

🐷 12.结语

🐷小生不才,今日发布的文章必有不足之处,大佬如若看出,恳请你在评论区发言指出,也可以私聊我!!!

感谢各位客官“食用”,码文不易,动动手指三连支持一下嘛!!!🐎🐎🐎
在这里插入图片描述

  • 19
    点赞
  • 12
    收藏
  • 打赏
    打赏
  • 14
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论 14

打赏作者

我好闲☞

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值