IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言】11.字符函数和字符串函数1 -> 正文阅读

[C++知识库]【C语言】11.字符函数和字符串函数1

字符函数和字符串函数

1. 求字符串长度

1. strlen 函数

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

了解 strlen 函数

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

int main()
{
	int len = strlen("abcdef");
	//字符串已经 '\0' 作为结束标志
	//strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。

	printf("%d\n", len);// 6

	return 0;
}
#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	int len = strlen(arr);
	printf("%d\n",len);//随机值
	// 遇到警告: 没有为字符串“arr”添加字符串零终止符
	// 因为没有设置到结束符“\0”
	// 因为strlen没有遇到结束符“\0”,所以一直计算数组长度直到遇到“\0”
	// 所以无法预测值,是随机值

	return 0;
}

打印两个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;
}
/*
	打印的是 str2 > str1
	因为 strlen函数 的返回类型是 size_t 类型
	size_t == unsigned int
	size_t 是无符号的int类型,所以进行加减操作得到的还是无符号数
	因为 strlen(str2) 的数小,strlen(str1)的数大
	strlen(str2) - strlen(str1) 得到是负数,但是因为是 无符号数,没有负数
	会当成正数编译,原反补码相同,所以是一个很大的数
	所以 strlen(str2) - strlen(str1) > 0 为真
	执行 printf("str2 > str1\n"); 语句
*/

自定义strlen 函数 - my_strlen 函数

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

int my_strlen(const char* str)//因为传入的是常量数组地址,不可变,用 const
{
	int count = 0;	//计算字符串的长度
	assert(str != NULL);	//检测传入地址参数是否为空

	while (*str != '\0')	//循环遍历每个字符串地址,若遇到'\0'停止循环
	{
		count++;	//每循环一次就+1,计算长度
		str++;	//对地址+1,跳到下一个地址,继续循环
	}
	return count;	//返回计算的数
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);

	printf("%d\n", len);//6

	return 0;
}

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

  • strcpy
  • strcat
  • strcmp

2.1 strcpy 函数

char* strcpy(char * destination, const char * source);
  • Copies the C string pointed by source into the array pointed by destination, including the terminating null character(and stopping at that point).
  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
  • 学会模拟实现。
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "abcdefghi";
	char arr2[] = "world";

	strcpy(arr1, arr2);	//第一个为存储复制内容的目标数组	第二个为要复制的字符串

	printf("%s\n", arr1);//world
	printf("%s\n", arr2);//world

	return 0;
}

自定义strcpy函数 - my_strcpy函数

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <assert.h>

char* my_strcpy(char* dest,char* src)
{
	assert(dest != NULL);
	assert(src != NULL);

    char* ret = dest;
	while (*src != '\0')	//遇到“\0”停止循环
	{
		*dest++ = *src++;	//将要复制的一个字符 放入 存储复制内容的目标数组
	}
	*dest = *src;//'\0'		最后将'\0'	放入 存储复制内容的目标数组
    
    return ret;
}

int main()
{
	char arr1[] = "abcdefghi";
	char arr2[] = "world";

	my_strcpy(arr1, arr2);	//第一个为存储复制内容的目标数组	第二个为要复制的字符串

	printf("%s\n", arr1);//world
	printf("%s\n", arr2);//world

	return 0;
}
//简化代码
#include <stdio.h>
#include <string.h>
#include <assert.h>

//返回值是char* 类型
char* my_strcpy(char* dest, const char* src)//常量指针,目的地地址会发生变化,但是源头地址不会发生变化,所以加const修饰更安全 
{
	assert(dest != NULL);
	assert(src != NULL);

	char* ret = dest;
	//拷贝src指向的字符串到dest指向的空间,包含'\0'

	//	while (*src != '\0')	//遇到“\0”停止循环
	//	{
	//		*dest++ = *src++;	//将要复制的一个字符 放入 存储复制内容的目标数组
	//	}
	//	*dest = *src;//'\0'		最后将'\0'	放入 存储复制内容的目标数组
	//}
	// 将代码合并
	// while循环将 复制 *src 的一个字符 放入 *dest 存储复制内容的目标数组
	// 然后++进入到下一个字符
	// 直到循环到 *src 的 '\0'
	// '\0'还是会赋值给 *dest
	// 括号的表达式依然保持 *dest 为 '\0'
	// while循环判断到 '\0' 为假,终止循环
	while (*dest++ = *src++)
	{
		;
		//*dest++ = *src++;
	}
	//*dest = *src;//'\0'
	//返回目的空间的起始地址
	return ret;
}

int main()
{
	char arr1[] = "abcdefghi";
	char arr2[] = "world";

	my_strcpy(arr1, arr2);	//第一个为存储复制内容的目标数组	第二个为要复制的字符串

	printf("%s\n", arr1);//world
	printf("%s\n", arr2);//world

	return 0;
}

strcpy函数 注意事项 1

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "abcdefghi";
	//char arr2[] = "world";
	//错误示范
	char arr2[] = { 'w','o','r','l','d' };
	strcpy(arr1, arr2);//错误原因:没有为字符串“arr2”添加字符串零终止符

	printf("%s\n", arr1);

	return 0;
}

strcpy函数 注意事项 2

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "a"; // 2字符(包含\0)
	char arr2[] = "world"; // 6个字符(包含\0)

	strcpy(arr1, arr2);
	/*
		写入到“arr1”时缓冲区溢出: 可写大小为“2”个字节,但写入了“6”个字节。
		因为arr1存放的是两个字符的内容,而arr2存放的是6个字符的内容
		将arr2 的复制到 arr1 上,会把arr1撑大,导致指针访问越界,
		指针访问的地址不是arr1的地址,报错
	*/

	printf("%s\n", arr1);

	return 0;
}

strcpy函数 注意事项 3

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	//char arr1[] = "abcdefghi"; //正确 空间是放在数组,是可以改变的
	//错误示范
	char *arr1 = "abcdefghi";//arr1 指向的是常量字符串,是不可变的
	//强行运行奔溃

	char arr2[] = "world";

	strcpy(arr1, arr2);

	printf("%s\n", arr1);

	return 0;
}

2.2 strcat 函数

char * strcat ( char * destination, const char * source );
  • Appends a copy of the source string to the destination string.The terminating null character in destination is overwritten by the first character of source, and a null - character is included at the end ofthe new string formed by the concatenation of both in destination.
  • 源字符串必须以‘\0’结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何 ?

strcat 函数 注意事项 1

目标空间必须有足够的大,能容纳下源字符串的内容。目标空间必须可修改。

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "hello";
	char arr2[] = "world";

	//代码错误
	strcat(arr1, arr2);
	//程序奔溃,本来arr1存放的是6个字符,要往arr1加入六个字符
	//arr1没有足够的空间存放
	//强制添加arr2的字符到arr1后面
	//追加之后造成程序奔溃
	//造成了越界访问
	
	printf("%s\n", arr1);

	return 0;
}

在这里插入图片描述

修改

//修改
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[30] = "hello";//将arr1变得足够大,可以放下追加的字符串
	char arr2[] = "world";

	strcat(arr1, arr2);

	printf("%s\n", arr1);

	return 0;
}

strcat 函数 注意事项 2

strcat函数注意事项:源字符串必须以‘\0’结束。

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[30] = "hello\0xxxxxxxx";
	char arr2[] = "world";//strcat函数要 arr2 也要“\0”结束

	strcat(arr1, arr2);
	//因为strcat函数在源字符串遇到“\0”的地方追加字符串
	//所以arr1遇到“\0”后在“\0”的地方(把源字符串“\0”覆盖)追加arr2
	//而arr2也把“\0”追加到arr1
	//而arr1后的xxxxxxxx因为前面的“world\0”覆盖掉而没有打印出来
	//但仍在arr1后面还有没有被覆盖的x

	printf("%s\n", arr1);//helloworld

	return 0;
}

在这里插入图片描述

在这里插入图片描述

自定义strcat函数

#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <assert.h>

char* my_strcat(char* dest, const char* src)	
{
	char* ret = dest;	// dest的地址赋值给ret
	assert(dest != NULL);
	assert(src != NULL);
	//assert(dest && src);

	//1.找到目标字符串的'\0'
	while (*dest != '\0')
	{
		dest++;	//当判断到'\0'停止,将dest的地址移动到'\0'前一个地址
	}
	//2.追加
	while (*dest++ = *src++)	//循环将 src 地址的字符串赋值 dest地址的'\0'(包含'\0')后面
	{
		;
	}
	return ret;	//返回一个char*类型,dest的数组的首元素地址
}

int main()
{
	char arr1[30] = "hello";
	char arr2[] = "world";

	my_strcat(arr1, arr2);
	printf("%s\n", arr1);

	return 0;
}

在这里插入图片描述

2.3 strcmp 函数

int strcmp(const char* str1, const char* str2, size_t num);
  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完

比较字符串错误示范

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

int main()
{
	char* p1 = "opqrst";
	char* p2 = "abcd";

	/*
		错误示范
		比较的不是字符串内容,而是字符串的首字符的地址
	*/
	if ("opqrst" >= "abcd")
	{
		printf("haha");
	}
	else
	{
		printf("hehe");
	}

	return 0;
}

strcmp函数使用

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

int main()
{
	char* p1 = "opqrst";
	char* p2 = "abcd";

	int ret = strcmp(p1, p2);
	printf("%d\n", ret);

	return 0;
}

strcmp函数返回值

// strcmp函数返回值
//	>0	
//	== 0
//	<0
#include <stdio.h>
#include <string.h>

int main()
{
	char* p1 = "qbc";
	char* p2 = "abc";

	if (strcmp(p1,p2) > 0)
	{
		printf("p1 > p2\n");
	}
	else if (strcmp(p1, p2) == 0)
	{
		printf("p1 == p2\n");
	}
	else if (strcmp(p1, p2) < 0)	//不一样的软件可能会返回不一样的数,写 <0 更好
	{
		printf("p1 < p2\n");
	}

	return 0;
}

自定义strcmp函数

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

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);//指针必须保持有效性,断言可以看出指针是否为空指针,空指针直接结束程序
	
    //比较
	while (*str1 == *str2)	//判断字符串有没有相同的
	{
		if (*str1 == '\0')	//直到第一个遇到 '\0',那第二个也是 '\0', '\0'表示结束,两个字符串相等
		{
			return 0;//相等
		}
		str1++;	//下一次循环的地址
		str2++;	//下一次循环的地址
	}

	//上一循环循环到没有相同的字符
	if (*str1 > *str2)	//判断第一个字符串是否大于第二个字符串
	{
		return 1;//大于
	}
	else
	{
		return -1;//小于
	}
}

int main()
{
	char* p1 = "abc";
	char* p2 = "abc";

	int ret1 = my_strcmp(p1, p2);
	printf("ret1 = %d\n",ret1);

	char* p3 = "abcdef";
	char* p4 = "abc";

	int ret2 = my_strcmp(p3, p4);
	printf("ret1 = %d\n", ret2);

	char* p5 = "abc";
	char* p6 = "abcdef";

	int ret3 = my_strcmp(p5, p6);
	printf("ret1 = %d\n", ret3);

	return 0;
}

3. 长度不受限制的字符串函数

3.1 注意点

strcpy 、strcat 、strcmp 一定要遇到 ‘\0’ 才停止运行,写程序要注意函数特性,避免写bug

//错误代码
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[5] = "abc";
	char arr2[] = "hello world";

    /*
    	strcpy 、strcat 、strcmp 长度不受限制,一定要遇到 '\0' 才停止运行,不管长度是否放得下,直接运行,会导致错误,所以写程序要注意函数特性,避免写bug
    */
	strcpy(arr1, arr2);
	printf("%s\n", arr1);

	return 0;
}

在这里插入图片描述

觉得长度不受限制的字符串函数会不安全,为了预防,就设置了长度受限制的字符串函数

3.2 strncpy 函数

strncpy和strcpy的差别

strcpy:		char* strcpy(char * destination, const char * source);
strncpy:	char* strncpy(char* destination, const char* source, size_t num);
Copies the first num characters of source to destination.
If the end of the source C string(which is signaled by a null - character) 
is found before num characters have been copied, 
destination is padded with zeros until a total of num characters have been written to it.
	
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

3.3 strncpy函数介绍

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

int main()
{
	char arr1[5] = "abc";
	char arr2[] = "hello world";
	strncpy(arr1, arr2, 4);
	printf("arr1:%s\n", arr1);
	/*
		arr1最后打印出来的是hell
		将 arr2 的 “hello world”的前四个复制覆盖到 arr1
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述


拷贝几个就覆盖目标数组的几个元素

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

int main()
{
	char arr1[10] = "abcdef";
	char arr2[] = "hello world";
	strncpy(arr1, arr2, 4);
	printf("arr1:%s\n", arr1);
	/*
		打印的是 helloef\0
		拷贝几个就覆盖目标数组的几个元素
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述

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

1

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

int main()
{
	char arr1[10] = "abcdef";
	char arr2[] = "jkl";
	strncpy(arr1, arr2, 6);
	printf("arr1:%s\n", arr1);
	/*
		打印的是: “j k l \0\0\0\0\0\0\0”
		因为要拷贝多少个就拷贝多少
		如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述


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

int main()
{
	char arr1[10] = "abcdefgh";
	char arr2[] = "jkl";
	strncpy(arr1, arr2, 6);
	printf("arr1:%s\n", arr1);
	/*
		打印的是: “j k l \0\0\0\0\0\0\0”
		因为要拷贝多少个就拷贝多少
		如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
		源字符串后面还有字符,也是在字符串后面
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述

3.4 strncat 函数

strncat函数介绍

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

int main()
{
	char arr1[30] = "hello";
	char arr2[] = "world";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	/*
		将 arr2 的内容追加到arr1 字符串后
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述


问题1:

“wor”后的“\0”的追加的还是怎么样?

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

int main()
{
	char arr1[30] = "hello\0xxxxxxxx";
	char arr2[] = "world";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	/*
		追加后的arr1,打印是 “hello wor\0 xxxx”
		本来arr1 的“\0”后的 x 有八个,追加了三个字符后还剩四个 x 字符
		所以得到的是追加完成后的“\0”是strncat函数追加完成后主动加上去的
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述


问题2:

要是追加超过字符串的长度会怎么样?

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

int main()
{
	char arr1[30] = "hello\0xxxxxxxxxxxx";
	char arr2[] = "world";
	strncat(arr1, arr2, 8);
	printf("%s\n", arr1);
	/*
		追加后的arr1,打印是 “hello world\0 xxxxxx”
		本来arr1 的“\0”后的 x 有十二个,追加了 world 字符 和 \0 后还剩 6个 x 字符
		所以得到的是追加完成的“\0”后就不再追加,
		所以后面的 6 个 x 字符没有被覆盖
		所以只会追加到 “\0”,\0 后不再追加
	*/

	return 0;
}

在这里插入图片描述


在这里插入图片描述

3.5 strncmp 函数

int strncmp(const char* str1, const char* str2, size_t num);
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
#include <stdio.h>
#include <string.h>

int main()
{
	//strncmp - 字符串比较
	const char* p1 = "abcdef";
	const char* p2 = "abcqwer";

	//int ret = strcmp(p1, p2, 3);
	int ret1 = strncmp(p1, p2, 3);
	printf("%d\n", ret1);// 0 
	//因为比较的是两个字符串前三个的字符
	//前三个字符相同,所以返回 0

	int ret2 = strncmp(p1, p2, 4);
	printf("%d\n", ret2);//-1
	//因为比较的是两个字符串前四个的字符
	//前三个字符相同,所以返回 -1

	return 0;
}

4. 字符串查找

4.1 strstr 函数

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

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.

返回str1中str2第一次出现的指针,如果str2不属于str1,则返回一个空指针。

strstr 函数的使用1

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

int main()
{
	char* p1 = "abcdef";
	char* p2 = "def";
	
	char* ret = strstr(p1, p2);
	if (ret == NULL)
		printf("字符串不存在");
	else
		printf("%s\n", ret);//def
	/*
	* 打印的是 def 的 d 的地址
	* 向后打印相应的字符
	*/

	return 0;
}

strstr 函数2

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

int main()
{
	char* p1 = "abcdefqhi";
	char* p2 = "def";

	char* ret = strstr(p1, p2);
	if (ret == NULL)
		printf("字符串不存在");
	else
		printf("%s\n", ret);//defqhi
	/*
	* 查找的字符串在源字符串的中间
	* 仍然找到 defqhi 的 d 的地址
	* 打印的是 defqhi 的 d 的地址
	* 向后打印相应的字符 - defqhi
	*/

	return 0;
}

strstr 函数3

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

int main()
{
	char* p1 = "abcdefabcdef";
	char* p2 = "def";

	char* ret = strstr(p1, p2);
	if (ret == NULL)
		printf("字符串不存在");
	else
		printf("%s\n", ret);//defabcdef
	/*
	* 查找的字符串在源字符串中有两个相同的
	* 仍然找到 第一个匹配的字符串 的 d 的地址
	* 打印的是 defabcdef 的 d 的地址
	* 向后打印相应的字符 - defabcdef
	*/

	return 0;
}

模拟实现strstr函数

简单版

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

char* my_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);
	assert(p2 != NULL);
	if (*p2 == '\0')//判断p2的地址是否只存在“\0”
	{
		return p1;//若p2是“\0”则返回p1的地址
	}
	while (*p1)//当*p1的地址循环到为“\0”时,结束循环
	{
		//while (*p1 == *p2)//当*p1和*p2的字符相同时进行while循环
		//进一步修改while循环
		// 判断的意思
		//以防*p1没有匹配到*p2的就到了“\0”
		//以防*p2没有查找完就到了“\0”
		//当* p1和* p2的字符相同时进行while循环
		while ((*p1 != '\0') && (*p2 != '\0') && (*p1 == *p2))
		{
			//各自地址加加,向后跳一个地址,使得这个while循环继续匹配下一个各自地址的字符
			p1++;
			p2++;
		}
		p1++;
		//若上一个while循环没有判断执行,
		//证明*p1地址的字符没有对应*p2地址的字符,
		//因此要加加,跳到下一个地址的字符,用下一个字符对应*p2地址的字符
	}
}

int main()
{
	char* p1 = "abcdef";
	char* p2 = "def";

	char* ret = my_strstr(p1, p2);

	if (ret == NULL)
	{
		printf("字串不存在");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

/*
* 不足点:
	因为匹配不成功,需要再次返回到第一个b的下一个地址重新查找匹配,
	但是p1p2已经加加了,不记得从那里开始查找的,所以不要轻易改变p1p2

*/

在这里插入图片描述

while ((*p1 != '\0') && (*p2 != '\0') && (*p1 == *p2))

在这里插入图片描述

修改模拟实现strstr函数

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

char* my_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);
	assert(p2 != NULL);

	char* s1 = p1;
	char* s2 = p2;
	char* cur = p1;//记录匹配到的所在位置
	if (*p2 == '\0')//判断p2的地址是否只存在“\0”
	{
		return p1;//若p2是“\0”则返回p1的地址
	}
	
	/*
	* 步骤1:判断cur是否为\0,不为0执行循环
	* 步骤2:cur的位置地址赋值给s1,是开始匹配的地址
	* 步骤3:p2的字符地址赋值给s2,是匹配的对象
	* 步骤4:第二个while循环,判断s1、s2是否匹配到\0,为\0就不执行while循环
	*		 而且s1地址的字符要和s2地址的字符匹配相同,不然也不执行while循环
	* 步骤5:s1、s2的地址加加,跳到下一个地址
	* 步骤6:if语句判断*s2为\0的话,则p2的已经到\0,查找函数应结束,返回cur的地址
	* 步骤7:cur加加,跳到下一个地址,记录
	* 步骤8:找不到相同匹配的字符串,返回NULL空指针
	* 
	* 修改后的my_strstr函数可以在第一次匹配不成功,可以回到开始匹配的位置
	* p1p2的地址固定不变,改变的是s1、s2,
	* 用cur记录匹配不成功然后到下一个字符地址的位置
	* 因此不用考虑因为无法返回原来查找的位置
	*/
	while (*cur)//当cur的不为\0执行下面循环
	{
		s1 = cur;//cur的位置地址赋值给s1,是开始匹配的地址
		s2 = p2;//p2的字符地址赋值给s2

		while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;//找到字串
	}
	return NULL;//找不到字串
}

int main()
{
	char* p1 = "abcdef";
	char* p2 = "def";

	char* ret = my_strstr(p1, p2);

	if (ret == NULL)
	{
		printf("字串不存在");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

在这里插入图片描述

运行警告修改

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

//再次优化

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

char* my_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);
	assert(p2 != NULL);

	char* s1 = (char*)p1;
	char* s2 = (char*)p2;
	char* cur = (char*)p1;//记录匹配到的所在位置
	if (*p2 == '\0')//判断p2的地址是否只存在“\0”
	{
		return (char*)p1;//若p2是“\0”则返回p1的地址
	}
	
	/*
	* 步骤1:判断cur是否为\0,不为0执行循环
	* 步骤2:cur的位置地址赋值给s1,是开始匹配的地址
	* 步骤3:p2的字符地址赋值给s2,是匹配的对象
	* 步骤4:第二个while循环,判断s1、s2是否匹配到\0,为\0就不执行while循环
	*		 而且s1地址的字符要和s2地址的字符匹配相同,不然也不执行while循环
	* 步骤5:s1、s2的地址加加,跳到下一个地址
	* 步骤6:if语句判断*s2为\0的话,则p2的已经到\0,查找函数应结束,返回cur的地址
	* 步骤7:cur加加,跳到下一个地址,记录
	* 步骤8:找不到相同匹配的字符串,返回NULL空指针
	* 
	* 修改后的my_strstr函数可以在第一次匹配不成功,可以回到开始匹配的位置
	* p1p2的地址固定不变,改变的是s1、s2,
	* 用cur记录匹配不成功然后到下一个字符地址的位置
	* 因此不用考虑因为无法返回原来查找的位置
	*/
	while (*cur)//当cur的不为\0执行下面循环
	{
		s1 = cur;//cur的位置地址赋值给s1,是开始匹配的地址
		s2 = (char*)p2;//p2的字符地址赋值给s2

		while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;//找到字串
	}
	return NULL;//找不到字串
}

int main()
{
	char* p1 = "abcdef";
	char* p2 = "def";

	char* ret = my_strstr(p1, p2);

	if (ret == NULL)
	{
		printf("字串不存在");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

疑问:若p2的字符串比p1的字符串长?

在这里插入图片描述

//再次优化

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

char* my_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);
	assert(p2 != NULL);

	char* s1 = (char*)p1;
	char* s2 = (char*)p2;
	char* cur = (char*)p1;//记录匹配到的所在位置
	if (*p2 == '\0')//判断p2的地址是否只存在“\0”
	{
		return (char*)p1;//若p2是“\0”则返回p1的地址
	}
	
	/*
	* 步骤1:判断cur是否为\0,不为0执行循环
	* 步骤2:cur的位置地址赋值给s1,是开始匹配的地址
	* 步骤3:p2的字符地址赋值给s2,是匹配的对象
	* 步骤4:第二个while循环,判断s1、s2是否匹配到\0,为\0就不执行while循环
	*		 而且s1地址的字符要和s2地址的字符匹配相同,不然也不执行while循环
	* 步骤5:s1、s2的地址加加,跳到下一个地址
	* 步骤6:if语句判断*s2为\0的话,则p2的已经到\0,查找函数应结束,返回cur的地址
	* 步骤7:cur加加,跳到下一个地址,记录
	* 步骤8:找不到相同匹配的字符串,返回NULL空指针
	* 
	* 修改后的my_strstr函数可以在第一次匹配不成功,可以回到开始匹配的位置
	* p1p2的地址固定不变,改变的是s1、s2,
	* 用cur记录匹配不成功然后到下一个字符地址的位置
	* 因此不用考虑因为无法返回原来查找的位置
	*/
	while (*cur)//当cur的不为\0执行下面循环
	{
		s1 = cur;//cur的位置地址赋值给s1,是开始匹配的地址
		s2 = (char*)p2;//p2的字符地址赋值给s2

		while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		if (*s1 == '\0')//修改,若s1提前到\0就提前结束程序
		{
			return NULL;
		}
		cur++;//找到字串
	}
	return NULL;//找不到字串
}

int main()
{
	char* p1 = "abcdef";
	char* p2 = "def";

	char* ret = my_strstr(p1, p2);

	if (ret == NULL)
	{
		printf("字串不存在");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

4.2 strtok 函数

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

函数特性

函数特性1

sep参数是个字符串,定义了用作分隔符的字符集合

一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

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

int main()
{
	char arr[] = "lqbz@bili.com";
	char* p = "@.";

	return 0;
}

/*
	第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
	char arr[] = "lqbz@bili.com";		char* p = "@.";
	                    |                      |
	char *strtok(  char *str,              const char *delim);
	
*/

在这里插入图片描述

函数特性2

strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改 变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。

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

/*
* 注意点:
	strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
	
	strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
	
	strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。

*/

int main()
{
	char arr[] = "lqbz@bili.com";
	char* p = "@.";

	/*
		lqbz @ bili . com
		strtok函数的第一个参数不为 NULL ,
		找到标记@,将@改成\0结尾,返回一个指向这个标记@的指针
		然后打印内容 lqbz ,遇到\0停止打印

	*/
	char* ret = strtok(arr, p);
	printf("%s\n", ret);

	/*
		lqbz @ ( @ - > '\0' )
		bili . com
		strtok函数的第一个参数为 NULL ,
		找到标记“.”,将“.”改成\0结尾,返回一个指向这个标记“.”的指针
		然后打印内容 bili ,遇到\0停止打印
	*/
	ret = strtok(NULL, p);
	printf("%s\n", ret);

	/*
		lqbz @
		bili . ( . - > '\0' )
		com\0
		strtok函数的第一个参数为 NULL ,
		然后打印内容com,遇到\0停止打印,后面没有内容可切割,停止strtok函数,返回NULL空指针
		然后打印内容 com ,遇到\0停止打印
	*/
	ret = strtok(NULL, p);
	printf("%s\n", ret);

	return 0;
}

在这里插入图片描述

在这里插入图片描述
GitHub代码

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 20:56:54  更:2022-10-22 21:00:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:48:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码