【C语言进阶】不会处理字符串?一万三千五百字包会保姆级教程

news2024/11/26 20:26:51

目录

😘前言😘:

一、字符串处理函数介绍🤯:

        1.strlen 函数🥎:

        2.strcpy 函数⚾:

        3.strcat 函数🏀:

        4.strcmp 函数🏈:

        5.strncpy 函数🏉:

        6.strncat 函数🎾:

        7.strncmp 函数🥏:

        8.strstr 函数🏑:

        9.strtok 函数🏓:

        10.strerror 函数⛳:

        11.memcpy 函数🥅:

        12.memmove 函数🎳:

        13.memcmp 函数🥌:

二、模拟库函数的实现🤔:

        1.模拟实现strlen 函数🥎:

        2.模拟实现strcpy 函数⚾:

        3.模拟实现strcat 函数🏀:

        4.模拟实现strcmp 函数🏈:

        5.模拟实现strstr 函数🏑:

        6.模拟实现memcpy 函数🥅:

        7.模拟实现memmove 函数🎳:

三、总结🥳:


🛰️博客主页:✈️努力学习的銮同学

🛰️欢迎关注:👍点赞🙌收藏✍️留言

🛰️系列专栏:💐【进阶】C语言学习

        家人们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

        关注我,关注我,关注我,你们将会看到更多的优质内容!!!


🏡🏡本文重点 🏡🏡:

🚅 字符串处理函数 🚃 模拟库函数🚏🚏

😘前言😘:

        期末考试将至,不知道各位小伙伴们都准备的怎么样了呢?在紧张的复习生活之余,也不要忘记了专业课的学习哟!前面三篇文章我们完整的将我们的指针技术进行了全面的升级,各位小伙伴们又向着大佬之路迈出了坚实的一步。

        本文我门将进入到下一部分的学习中去。通过本课,我们将更加深刻的理解各种字符串函数的处理方式和使用方法,助你工作面试一臂之力!

一、字符串处理函数介绍🤯:

        我们在C语言的从程序代码编写中,对字符和字符串的处理相当频繁,但是我们还知道,C语言本身并没有字符串类型。而字符串通常放在【常量字符串】或者【字符数组】中。其中,字符串常量适用于那些对它不做修改的字符串函数

        知道了字符串在计算机中的储存原理之后,我们就正式开始介绍字符串的使用。

        1.strlen 函数🥎:

        strlen 函数是大家的老朋友了,想必大家也都不陌生了。

        strlen 函数(string length)的作用是用于返回字符串中结束标识符 ' \0 ' 之前出现的的字符个数,因此,strlen函数所处理的字符串对象必须是以结束标识符 ' \0 ' 结尾的字符串。其返回值类型为 size_t 类型,该类型为无符号类型

        我们来看 strlen 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
    //两中可行的初始化方式:
	char arr1[]="Hellow!";
	char arr2[] = { 'H','e','l','l','o','w','!','\0' };
	
	int ret1 = strlen(arr1);
	int ret2 = strlen(arr2);

	printf("The length of arr1 is %d\n", ret1);
	printf("The length of arr2 is %d\n", ret2);

	return 0;
}

        我们使用字符型数组将字符串储存起来,接着使用 strlen 函数计算字符串 " Hellow! " 中所有字符的数量(使用双引号初始化字符串时,编译器将会自动在最后添加上结束标识符),并可以使用使用一个整型变量接收 strlen 函数的返回值,进行打印。

        并且我们还说到,strlen 函数的返回类型为无符号数,因此 strlen 函数不可以直接用来比较两个字符串的大小。例如:

#include<stdio.h>
#include<string.h>

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";

	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2 > str1\n");
	}
	else
	{
		printf("srt1 < str2\n");
	}
	return 0;
}

         我们可能会认为计算出的结果为 3 - 6 为 -3,结果会执行 else 语句。但事实上,因为 strlen 函数的返回类型为无符号类型,得出的结果也是无符号类型,于是原本第一位即符号位上的数字也将被作为数据的一部分,因而实际得出的结果为一个非常大的整数,执行了 if 语句

        于是如果我们想要使用 strlen 函数来判断字符串的大小,可以通过让它的返回值由无符号数变为有符号数即可,即强制类型转换来实现:

#include<stdio.h>
#include<string.h>

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";

	if ((int)strlen(str2) - (int)strlen(str1) > 0)
	{
		printf("str2 > str1\n");
	}
	else
	{
		printf("srt1 < str2\n");
	}
	return 0;
}

        2.strcpy 函数⚾:

        在之前的学习中,strcpy 函数也是我们经常使用的字符串处理函数之一。

        strcpy 函数(string copy)的作用是,可以将字符串从源地址复制至目的地址,通俗来讲就是用来实现字符串的复制和拷贝。并且它会将源地址内的结束标识符 ' \0 ' 一并拷贝过去,因此源地址必须以 ' \0 ' 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的

        我们来看 strcpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[] = "Hellow!";
	const char arr2[10] = { 0 };

	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
	printf("\n");

	strcpy(arr2, arr1);
    //strcpy函数使用格式为:strcpy(目的地址, 源地址);

	printf("After copy , the char inside arr1 are %s\n", arr1);
	printf("After copy , the char inside arr2 are %s\n", arr2);

	return 0;
}

        我们初始化字符数组 arr 中的内容,并使 arr2 中内容为空。首先我们将两个字符串进行打印,验证拷贝前两字符串中的存储内容。接着我们使用 strcpy 函数将字符数组 arr1 中的字符串拷贝至 arr2 中,并在此打印两数组中的内容对拷贝结果进行验证。

        我们要特别注意的是 strcpy 函数返回的是目标空间的起始地址,该函数设置返回值值类型的目的是为了实现链式访问(以后会讲到,这里仅做了解)。

        3.strcat 函数🏀:

        这个字符串处理函数就比较陌生了,在我们之前的学习中没有用到过,甚至不曾见过,我们现在就来一起学习它。

        strcat 函数(string catenate)的作用是,将源地址的字符串追加补充至目的地址处。与字符串拷贝函数相同,它在进行补充追加时是从目的地址的结束标识符处 ' \0 ' 开始追加的,追加至源地址的结束标识符处停止。且它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的

        我们来看 strcat 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[20] = "Hellow!";
	const char arr2[20] = "Welcome!";

	printf("Before catenate , the char inside arr1 are %s\n", arr1);
	printf("Before catenate , the char inside arr2 are %s\n", arr2);
	printf("\n");
	
	strcat(arr2, arr1);
	
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);

	return 0;
}

        我们同样首先定义并初始化两个字符数组,接着打印他们验证他们各自内部的存储内容。然后通过使用stract 函数,我们将字符数组 arr21中的内容,成功的追加补充到了字符数组 arr1 中,并再次进行了打印,验证追加补充的结果。

        注意,strcat 函数无法追加自己。原因很好理解,我们在定义时就已经固定了字符数组的储存空间了,当追加自己时,相当于将自己与和与自己相同大小的字符数组,即两倍自身大小的数据放入自己的储存空间中,可想而知一定是不可行的。

        4.strcmp 函数🏈:

        strcmp 函数(string compare)的作用为按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到结束标识符 ' \0 ' 或对应位置的字符不同。若比较至结束标识符都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串

        我们来看 strcmp 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";

	int ret = strcmp(arr1, arr2);
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数

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

	return 0;
}

        首先我们定义并初始化两个字符数组,接着对两个数组进行比较,根据 strcmp 函数的比较结果得到返回值,再根据返回值反馈我们想要的字符串比较结果。

        这里的要点是,该函数的作用并不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

        5.strncpy 函数🏉:

        strncpy函数(string number copy)的作用为将指定长度的字符串复制到字符数组中,即表示把源地址中字符串开始的前n个字符拷贝到目的地址中。与 strcpy 相同,它同样会将源地址内的结束标识符 ' \0 ' 一并拷贝过去,因此源地址必须以 ' \0 ' 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的

        我们来看 strncpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

//strncpy函数的使用:
int main()
{
	const char arr1[] = "Hellow!Welcome!";
	const char arr2[10] = { 0 };

	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);

	strncpy(arr2, arr1, 7);

	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);

	return 0;
}

        我们看到,使用该函数,我们成功的将字符数组 arr1 中的前七个字符拷贝到了 字符数组 arr2 中,并且通过前后两次的反馈打印验证了按指定长度拷贝操作成功完成。

        注意:若源字符串的长度小于我们传递过去的参数,则拷贝完源字符串之后,将会在目标后追加字符 ' 0 ',直到拷贝至参数规定个数

        6.strncat 函数🎾:

        strncat 函数(string num catenate)的作用为从源地址处将指定长度的字符串追加补充到目的地址中。与 strcat 函数类似,它在进行补充追加时也是从目的地址的结束标识符处 ' \0 ' 开始追加的,不同的是追加至参数限制的字符数处停止。但它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下出家补充的字符串,同时目的地址的空间必须是可变、可修改的

        我们来看 strncat 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[] = "Hellow!Welcome!";
	const char arr2[20] = "Welcome!";

	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);

	strncat(arr2, arr1, 7);

	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);

	return 0;
}

        我们可以看到,首先定义并初始化两个字符数组,接着打印追加补充之前它们各自空间内的内容进行确认,然后我们使用了 strncat 函数有限制的从数组 arr1 向 arr2 中追加补充了七个字符,最后进行了打印反馈,验证了我们的有限制追加补充结果。

        并且我们可以想到,通过限制长度我们就可以实现字符数组对自己的追加补充了。

        7.strncmp 函数🥏:

        strncmp 函数(string number compare)的作用为有限制的按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到参数限制位数位置上全部比较结束或对应位置的字符不同。若参数限制位数位置上的字符都比较结束且都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串

        我们来看 strncmp 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";

	int ret = strncmp(arr1, arr2, 3);
    //比较前 3 个字符
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数

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

	return 0;
}

        其作用原理与作用过程,与 strncmp 函数十分类似,唯一不同便是限制了字符比较的位数,所以在此不再做过多的阐述,可以参考 strncmp 函数进行理解。

        但是同样要注意是,该函数的作用也不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

        8.strstr 函数🏑:

        strstr 函数(string string)的作用为从一个字符串中寻找其字串,通俗来讲就是从一个字符串中寻找另一个字符串。若找到目标字串则返回指向目标字串的指针,若没有找到目标字串则返回空指针(不返回)。

        我们来看 strstr 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	const char arr1[] = "abcdefg";
	const char arr2[] = "cde";

	char* ret = strstr(arr1, arr2);
	//从ar1中寻找arr2
	if (ret == NULL)
	{
		printf("找不到该字符串!\n");
	}
	else
	{
		printf("成功找到该字符串'%s'!\n", ret);
	}

	return 0;
}

        我们定义并初始化了两个字符数组,并使用 strstr 函数进行字串寻找处理,并使用字符型指针接收汉函数的返回值,最后,对接收了返回值的指针 ret 进行判断,若为空则确定为没有从数组 arr1 中找到字串 arr2,否则便通过返回值,使用指针对字串地址内的数据进行访问

        该函数数有返回值,支持进行函数的链式访问

        9.strtok 函数🏓:

        strtok 函数(string token)的作用为将字符串分解为一组字符串。听起来似乎很难理解,其实很简单。该函数有两个数组作为参数,它的实际作用便是将其中一个数组组为分割数组,在另一个数组中寻找这些“分割符”,并在分割符处将这个数组内的字符串加上结束标识符 ' \0 ' ,将其分割成一组(多个)字符串。若第一个参数不为 NULL ,将找到字符数组中的第一个标记并保存它在字符串中的位置;若第一个参数为 NULL ,将在同一个字符串中被保存的位置开始,查找下一个标 记

        我们来看 strtok 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "1254594572@QQ.COM";
	char arr2[30] = { 0 };
	strcpy(arr2, arr1);

	const char* arr3 = "@.";

	printf("账号:%s\n", strtok(arr2, arr3));
	//找到第一个标记停止
	printf("服务商:%s\n", strtok(NULL, arr3));
	//从保存好的位置开始往后找
	printf("网址后缀:%s\n", strtok(NULL, arr3));
	//从保存好的位置开始往后找

	return 0;
}

        首先我们要知道,strtok 函数是会对数组本身进行操作的,所以我们为了保护原始数据,在定义并初始化好字符数组之后,又定义了一个新的数组并将原始数据拷贝过去,作为临时拷贝供我们进行操作。接着我们定义并初始化了分割符数组,函数将根据分割符数组 arr3 对 临时拷贝 arr2 进行分割第一次执行函数时之前没有标记,于是直接进行操作找到第一个标记并分割打印。此时就已经存在标记了,再连续两次找到前一次执行作下的标记按照分割符将数组分割完毕并打印。

        但是上面的代码风格较为简陋、复杂,我们可以将上面段代码优化为:

#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "1254594572@QQ.COM";
	char arr2[30] = { 0 };
	strcpy(arr2, arr1);

	const char* arr3 = "@.";

	char* str = NULL;
	for (str = strtok(arr2, arr3); str != NULL; str = strtok(NULL, arr3))
	{
		printf("%s\n", str);
	}

	return 0;
}

        注意,若字符串中不存在更多的标记,则返回 NULL 指针

        10.strerror 函数⛳:

        strerror函数(string error)的作用为返回错误码对应的信息。即根据接收到的错误码(错误码 errno 为全局变量),返回错误原因的详细信息

        我们来看 strerror 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	int i = 0;
	for (i = 0; i <= 4; i++)
	{
		printf("错误原因为:%s\n", strerror(i));
	}

	return 0;
}

        11.memcpy 函数🥅:

        memcpy函数(memory copy)的作用为从源内存空间向目的内存空间拷贝限制数量(单位是字节)的数据。它与 strcpy 函数类似,作用均为拷贝数据,不同的是 strcpy 仅仅只操作字符串故会在结束标识符 ' \0 ' 处停止,而 memcpy 函数操作的是内存,内存中的数据是相邻的,故不会在结束标识符处停止

        我们来看 memcpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

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

	printf("Before copy , the char inside arr2 are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");

	memcpy(arr2, arr1, 20);

	printf("After copy , the char inside arr2 are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");

	return 0;
}

        我们首先创建并初始化了两个整形数组,接着打印出数组 arr2 内数据的存放情况。紧接着我们通过内存拷贝函数,将数组 arr1 内的前20个字节的数据拷贝给了 arr2数组。整型数组内每个数据元素所占的内存空间为4个字节,20个字节即将数组 arr1 中的前五个数据元素拷贝给了数组 arr2。最后再次打印数组 arr2 中的数据,验证拷贝结果。

        在这里各位小伙伴们要注意了,如果源内存空间和目标内存空间有任何的重叠,复制的结果都是未定义的

        12.memmove 函数🎳:

        memmove函数(memory move)的作用为弥补 memcpy 函数的不足,主要用于处理内存的重叠部分。即如果源空间和目标空间出现重叠,就得使用 memmove 函数来进行处理

        我们来看 memcpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	
	printf("Before copy , the char inside arr are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");
	
	memmove(arr+2, arr, 20);
	
	printf("After copy , the char inside arr are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");

	return 0;
}

        这个函数与 memcpy 函数除处理对象不同外,语法结构和使用场景等都极其类似,这里也就不再做过多赘述。

        13.memcmp 函数🥌:

        memcmp 函数(memory compare)的作用与 strcmp 函数的作用类似,不过 memcmp 函数是从内存的角度以字节为单位进行处理,故 memcmp 函数同样需要第三个参数进行限制,而不会在结束标识符 ' \0 ' 处停止比较。其它方面也就不再做过多赘述。

         我们来看 memcmp 函数的基本使用方式:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	int arr2[] = { 1,2,3,4,5,9,6,7,8 };

	int ret = memcmp(arr2, arr1, 24);

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

	return 0;
}

        这个函数比较简单,也没有什么需要特别注意的地方,故不再做赘述。

二、模拟库函数的实现🤔:

        1.模拟实现strlen 函数🥎:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* p)
{
	int count = 0;
	assert(p != NULL);
	//进行空指针校验,防止出现空指针
	while (*p != '\0')
	{
		count++;
		p++;
	}
	return count;
}

int main()
{
	const char arr[] = "Welcome!";
	int ret = my_strlen(arr);
	printf("The length of arr is %d\n", ret);

	return 0;
}

        2.模拟实现strcpy 函数⚾:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

char* my_strcpy(char* p2, const char* p1)
{
	assert(p2 != NULL);
	assert(p1 != NULL);
	//等价于assert(p2 && p1);

	char* ret = p2;
    //将目的地址作为返回值

	while (*p2++ = *p1++)
	{
		;
	}

    //返回目的地址的作用是为了实现链式访问
    return ret;
}

int main()
{
	const char arr1[] = "Welcome!";
	const char arr2[10] = { 0 };

	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
	printf("\n");

	my_strcpy(arr2, arr1);

	printf("After copy , the char inside arr1 are %s\n", arr1);
	printf("After copy , the char inside arr2 are %s\n", arr2);

	return 0;
}

        3.模拟实现strcat 函数🏀:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

char* my_strcat(char* p2, char* p1)
{
	assert(p2 && p1);
	char* ret = p2;
	//查找目标空间中的结束标识符,找到后停止
	while (*p2)
	{
		p2++;
	}
	//从结束标识符开始追加
	//追加:
	while (*p2++ = *p1++)
	{
		;
	}
	return ret;
}

int main()
{
	const char arr1[20] = "Hellow!";
	const char arr2[20] = "Welcome!";

	printf("Before catenate , the char inside arr1 are %s\n", arr1);
	printf("Before catenate , the char inside arr2 are %s\n", arr2);
	printf("\n");
	
	my_strcat(arr2, arr1);
	
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);

	return 0;
}

        4.模拟实现strcmp 函数🏈:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

//模拟实现strcmp函数:
int my_strcmp(const char* p1, const char* p2)
{
	assert(p1 && p2);
	//找到字符不同的对应位:
	while (*p1 == *p2)
	{
		if (*p1 == '\0')
		{
			return 0;
		}
		p1++;
		p2++;
	}
	if (*p1 > *p2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";
	
	int ret = my_strcmp(arr1, arr2);
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数
	
	if (ret = 1)
	{
		printf("arr1 > arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1 = arr2\n");
	}
	else
	{
		printf("arr1 < arr2\n");
	}

	return 0;
}

        5.模拟实现strstr 函数🏑:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

char* my_strstr(char* p1, char* p2)
{
	assert(p1 && p2);

	char* pp1 = p1;
	char* pp2 = p2;

	char* cur = p1;
	while (*cur)
	{
		pp1 = cur;
		pp2 = p2;
		while (*pp1 && *pp2 && (*pp1 == *pp2))
		{
			pp1++;
			pp2++;
		}
		if (*pp2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;
}

//模拟strstr函数的实现:
int main()
{
	const char arr1[] = "abcdefg";
	const char arr2[] = "cde";

	char* ret = my_strstr(arr1, arr2);
	//从ar1中寻找arr2
	if (ret == NULL)
	{
		printf("找不到该字符串!\n");
	}
	else
	{
		printf("成功找到该字符串'%s'!\n", ret);
	}

	return 0;
}

        6.模拟实现memcpy 函数🥅:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

void* my_memcpy(void* p2, const void* p1, size_t count)
{
	assert(p2 && p1);
    void* ret = p2; 
	while (count--)
	{
		*(char*)p2 = *(char*)p1;
		p2 = (char*)p2 + 1;
		p1 = (char*)p1 + 1;
	}
    return ret;
}

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

	printf("Before copy , the char inside arr2 are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");

	my_memcpy(arr2, arr1, 20);

	printf("After copy , the char inside arr2 are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");

	return 0;
}

        7.模拟实现memmove 函数🎳:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<assert.h>

void* my_memmove(void* p2, const void* p1, size_t count)
{
	assert(p2 && p1);
	void* ret = p2;
	if (p2 < p1)
	{
		while (count--)
		{
			*(char*)p2 = *(char*)p1;
			p2 = (char*)p2 + 1;
			p1 = (char*)p1 + 1;
		}
	}
	else
	{
		while (count--)
		{
			*((char*)p2 + count) = *((char*)p1 + count);
		}
	}
	return ret;
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };

	printf("Before copy , the char inside arr are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");

	my_memmove(arr + 2, arr, 20);

	printf("After copy , the char inside arr are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");

	return 0;
}

三、总结🥳:

        今日爆肝十小时,一万三千余字💦💦,呕心沥血为大家整理并讲解了所有常用字符串操作函数,以及其中部分库函数的自定义模拟实现,希望对大家的学习有所帮助。各位小伙伴们课下也不要过于放松喔,勤加练习才能造就我们夯实的基础和精进的编程技术💯💯。感谢大家的阅读,我们下节课再见叭🍸🍸

        🔥🔥在真实的生命里,每桩伟业都由信心开始,并由信心跨出第一步🔥🔥

        更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~  你们的点赞和关注对我真的很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/92383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WSL_03 WSL2 从C盘迁移到D盘

文章目录1 动机1 查看虚拟机状态&#xff0c;并关闭要迁移的虚拟机2 迁移WSL22.1 出现的问题&#xff1a;已存在具有提供的名称的分发(已解决)3 设置启动时的默认用户&#xff0c;没有设置默认为root参考1 动机 WSL2默认安装在C盘中&#xff0c;win R运行中使用%localappdata…

python科学计算 之 Numpy库的使用详解

目录 一&#xff1a;Numpy简介 二&#xff1a;ndarray的创建 三&#xff1a;ndarray的属性 四&#xff1a;切片 索引 五&#xff1a;数组的轴(二维数组) 六&#xff1a;二维数组 切片索引 七&#xff1a;高级索引 八&#xff1a;Numpy广播 九&#xff1a;ufunc函数 算…

使用pypy来提升你的python项目性能

一、PyPy介绍 PyPy是用Python实现的Python解释器的动态编译器&#xff0c;是Armin Rigo开发的产品&#xff0c;能够提升我们python项目的运行速度。PyPy 是利用即时编译的 Python 的替代实现。背后的原理是 PyPy 开始时就像一个解释器&#xff0c;直接从源文件运行我们的 Pyth…

Revit二次开发小技巧(十五)构件的最小矩形外轮廓

前言&#xff1a;我们会经常遇到需要计算一个构件的最小外轮廓&#xff0c;一般直接取BoundingBox只有最大和最小值坐标&#xff0c;也是基于x-y坐标系下的。往往不是最小的矩形&#xff0c;所以分享下面的算法来计算最小的外轮廓&#xff0c;条件为法向量是指向Z轴的&#xff…

mqtt服务器搭建与qt下的mqtt客户端实现

一、mqtt介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的&#xff0c;这些特点使它适用范围非常广泛。在很多情…

前端基础(九)_CSS的三大特征

CSS的三大特征 1、层叠性 1.样式冲突&#xff0c;遵循就近原则 2.样式不冲突&#xff0c;不会层叠&#xff0c;会叠加 1.1.样式冲突&#xff0c;遵循就近原则例子&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UT…

[附源码]Nodejs计算机毕业设计基于的服装商城系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Java优雅的记录日志:log4j实战篇

写在前面 项目开发中&#xff0c;记录错误日志有以下好处&#xff1a; 方便调试 便于发现系统运行过程中的错误 存储业务数据&#xff0c;便于后期分析 在java中&#xff0c;记录日志有很多种方式&#xff1a; 自己实现&#xff1a;自己写类&#xff0c;将日志数据&#xf…

HTTP Range:范围请求

文章目录HTTP 范围请求HTTP 范围请求 Range 头是在 HTTP/1.1 协议中新增的一个请求头。包含 Range 头的请求通常称为范围请求&#xff0c;因为 Range 头允许服务器只发送部分响应到客户端&#xff0c;它是下载工具&#xff08;例如迅雷&#xff09;实现多线程下载的核心所在&a…

Python -- 列表

目录 1.列表的基本使用 1.1 列表的格式 1.2 使用下标获取列表元素 2.列表的增删改查 2.1 添加元素 2.2 修改元素 2.3 查找元素 2.4 删除元素 2.5 排序&#xff08;sort、reverse&#xff09; 3.列表遍历 3.1 使用while循环 3.2 使用for循环 4.列表的嵌套 1.列表的基本…

面向切面编程 AOP

AOPAOP的概念AOP &#xff08;底层原理&#xff09;AOP 底层使用动态代理AOP &#xff08; JDK 动态代理&#xff09;首先我们来看一下 Spring 的百度百科   Spring 框架是一个开放源代码的 J2EE 应用程序框架&#xff0c;由 Rod Johnson 发起&#xff0c;是针对 Bean 的生命…

cpp项目中遇到的一些错误

1.解决由于找不到xxx.dll&#xff0c;无法继续执行代码的问题 解决由于找不到xxx.dll&#xff0c;无法继续执行代码的问题_happylife_mini的博客-CSDN博客_由于找不到emp.dll无法继续执行代码在用vs写项目&#xff0c;或者你下载github上的C代码的时候&#xff0c;是不是经常遇…

【Redis技术探索】「底层架构原理」探索分析服务系统的网络架构和线程模型

Redis网络基础架构 网络编程离不开Socket&#xff0c;网络I/O模型最常用的无非是同步阻塞、同步非阻塞、异步阻塞、异步非阻塞&#xff0c;高性能网络服务器最常见的线程模型也就是基于EventLoop模式的单线程模型。 Redis基础组建结构 Redis网络层基础组件主要包括四个部分&a…

acm是什么?你准备好去打了吗?(未完结)

1.引言2.acm究竟是什么&#xff1f;3.acm的时间安排4.acm该如何准备1.引言 作为一个零基础的小白&#xff0c;acm这条路走的并不顺畅&#xff0c;接触的信息很少&#xff0c;以至于在这条道路上走了不少弯路&#xff0c;浪费了大量的时间&#xff0c;现在也快要退役的阶段&…

Linux基础-软件包管理器RPM与yum

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【参考文章】 参考文章&#xff1a;https://howard2005.blog.csdn.net/article/details/127131286?spm1001.2014.3001.5502 文章目录一、使用RPM软件包管理器1. RPM安装软件包2. RPM更新与升级软件包3. RPM查询软件包…

Qt-Web混合开发-Qt读写Json数据(5)

Qt-Web混合开发-Qt使用内置json库读写json示例&#x1f34f; 文章目录Qt-Web混合开发-Qt使用内置json库读写json示例&#x1f34f;1、概述&#x1f353;2、实现效果&#x1f345;3、实现功能&#x1f95d;4、关键代码&#x1f33d;5、源代码&#x1f346;更多精彩内容&#x1f…

面试怎么回答MySQL索引问题,看这里

前言 小A在宿舍里跟哥们开五黑打排位中&#xff0c;突然收到女神小美的消息&#xff1a;“小A&#xff0c;我今天面试碰到索引问题了&#xff0c;我没回答好”。小A顾不上游戏抓紧回复到&#xff1a;“到你宿舍某某咖啡店吧&#xff0c;我帮你一起看下”。 小A抓紧时间换了衣…

物联公司网页设计制作 简单静态HTML网页作品 静态企业网页作业成品 学生网站模板

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Linux系统部署

Linux系统部署 下载vmware centos7 xshell6 xftp6新建虚拟机&#xff0c;注意设置网络连接&#xff0c;设置登录名&#xff1a;root,密码&#xff1a;root,等待登录&#xff0c;输入用户名和密码&#xff08;注意密码输入不显示&#xff09;登录成功&#xff0c;执行命令Ifc…

【网管日记】MySQL主从复制

MySQL主从复制 基本介绍 MySQL 主从复制是一个异步的复制过程&#xff0c;底层是基于 Mysql 数据库自带的 二进制日志 功能。 一台或多台 MySQL 数据库&#xff08;slave&#xff0c;即 从库 &#xff09;从另一台 MySQL 数据库&#xff08; master&#xff0c;即 主库 &…