字符函数和字符串函数(2)


一、strstr(判定一个字符串是否包含另一个)

strstr(a,b):判定a是否包含b,或者说b是否是a的字符串子串(子字符串)例如a=hello world,b=llo。
在算法中有一些专门的方法来实现字符串的包含关系判定的,KMP算法,KMP只是理论上更高效,实际开发中,大部分情况下,暴力搜索并不比KMP更慢,a和b都非常长的时候,KMP确实更快。而实际开发中a很长,b比较短的情况更常见。
1.先创建black指针指向a字符串的起始位置,创建一个sub指针指向b字符串的起始位置。
2.对比black指针和sub指针指向的字符是否相等。
3.如果不相等,就直接black++。
4.如果相等,创建一个red指针,指向black,使用red和sub指针进行比较,依次往后遍历。
5.如果red和sub相等,red++,sub++,直到sub遇到\0,此时说明找到了匹配的子串,直接返回black。
6.如果red和sub不相等,让red返回b的初始位置,同时black指针也++。
在这个过程中有两个小细节要注意区分:
1.char* str=NULL 空指针
char* str="" 空字符串
2.可以把char赋值给const char,但是不能把const char赋值给char
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
const char* myStrstr(const char* str1, const char* str2) {
	assert(str1 != NULL);
	assert(str2 != NULL);
	assert(str1 != '\0');
	assert(str2 != '\0');
	const char* black =str1;
	//外层循环通过black控制从str1的哪个位置开始找子串
	while (*black != '\0') {
		const char* red = black;
		const char* sub = str2;
		//内层循环就是从black开始,判断当前这个子串和str2是否相等
		while (*red != '\0' && *sub != '\0'&&*red==*sub) {
			red++;
			sub++;
		}
		//以上循环结束有三种可能
		//1.*red=='\0',可以直接结束循环,直接进入下次循环
		//2.*sub=='\0',找到了,直接返回black
		//3.*red!=*sub,直接进入下次循环
		if (*sub == '\0') {
			return black;
		}
		black++;
	}
	return NULL;
}
int main() {
	char* str1 = "hello world";
	char* str2 = "llo";
	const char* result = myStrstr(str1, str2);
	printf("%c\n", *result);
	system("pause");
	return 0;
}

在这里插入图片描述

二、strtok(字符串切分)

这个过程要用到标记:把一个整体的东西给分成几个部分,每个部分都是用一个标记来表示这个部分的初始位置。
代码如下(示例):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
	char str[] = "aaaa bbbb cccc dddd";
	char* pch = strtok(str, " ");
	while (pch != NULL) {
		printf("%s\n", pch);
		pch = strtok(NULL, " ");
	}
	system("pause");
	return 0;
}

在这里插入图片描述
下面进行过程解释:
第一次调用:strtok(str," “): 从str开始(第一个a开始),往后找“ ”,当遇到“ ”的时候就返回,返回第一个a,同时把刚才的“ ”改为\0。
第二次调用:strtok(NULL,” “) 此时发现第一个参数为NULL,表示从上次切分的结果位置继续往下切分,从刚才的\0位置继续往后找空格,当找到b后面的位置的时候,就遇到了空格,仍然把空格改为\0,并且,返回刚才第一个b的位置。
第三次调用:strtok(NULL,” “) 第一个参数仍然是NULL,继续从上次的结束位置往后找,找到最后一个c的位置遇到空格,把空格改为\0,同时返回第一个c的位置。
第四次调用:strtok(NULL,” “) 第一个参数仍然是NULL,继续从上次的结束位置往后找,找到最后一个d的位置,找到了\0,函数返回第一个d的位置。
第五次调用:strtok(NULL,” ") 第一个参数仍然是NULL,继续从上次结束位置往后找,上次的位置后面没东西了,找完了就说明整个字符串切分完毕,此时直接返回NULL。
strtok的缺点:
1.需要多次调用才可完成功能,使用起来比较复杂
2.多次调用的过程中,参数不同
3.调用过程中会破坏原来的字符串
4.strtok内部使用静态变量记录上次调用的位置,导致线程不安全,所以实际开发中不建议使用strtok.(C语言中,strtok_r在参数里由程序员手动指定一个内存用来保存上次的切分位置,这样程序员就可以针对自己创建的变量通过加锁等方式来控制线程安全。)
在这里插入图片描述

三、memcpy(把一块内存中的数据拷贝到另一个内存中)

这里要提前声明,memcpy和strcpy以及字符串之间都是没有任何关系滴。str系列的函数,头文件是string.h,memory系列的函数,也在string.h中,这本身就是不科学滴。
在这里插入图片描述
代码如下(示例):

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* myMemcpy(void* dest, const void* src, size_t num) {
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);
	//按照字节为单位进行拷贝
	char* cdest = (char*)dest;
	const char* csrc = (char*)src;
	for (size_t i = 0; i < num; i++) {
		cdest[i] = csrc[i];
	}
	return dest;
}
int main() {
	char str[] = "hello";
	char str2[1024] = { 0 };
	myMemcpy(str2, str, sizeof(str));
	printf("%s\n", str2);
	system("pause");
	return 0;
}

在这里插入图片描述
这里对void做出解释,void是一种特殊的指针,只知道地址,不知道大小,各种类型的指针都可以赋值给这个void*,所以可用于泛型编程。
假如对于结构体使用memmove,示例如下:

typedef  struct Student {
	int id;
	char name[1024];
}Student;
int main() {
	Student s1 = { 1,"zhangsan" };
	Student s2;
	myMemcpy(&s2, &s1, sizeof(Student));
	printf("%d,%s\n", s2.id, s2.name);
	system("pause");
	return 0;
}

在这里插入图片描述

四、memcmp(比较两个内存中内容的“大小”关系)

这种的比较没啥意义,比较规则类似于字符串的字典序。
在这里插入图片描述

五、memmove(拷贝n个字节到目标地址)

文档上,memcpy不能处理内存重叠的情况,但是实际上,很多编译器实现的时候,已经处理了。很多编译器memcpy==memmove。memmove就是为了解决内存重叠的情况。
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* myMemmove(void* dest, const void* src, size_t num) {
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);
	char* cdest = (char*)dest;
	const char* csrc = (const char*)src;
	if (csrc <= cdest && cdest <= csrc + num) {
		//如果内存重叠了,就倒着拷贝
		for (size_t i = num; i > 0; i--) {
			cdest[i - 1] = csrc[i - 1];
		}
		return dest;
	}
	else {
		//如果不重叠,正着拷贝
		for (size_t i = 0; i < num; i++) {
			cdest[i] = csrc[i];
		}
		return dest;
	}
}
int main() {
	int arr[] = { 1,2,3,4 };
	int arr2[100] = { 0 };
	myMemmove(arr2, arr, sizeof(arr));
	for (int i = 0; i < 4; i++) {
		printf("%d\n", arr2[i]);
	}
	system("pause");
	return 0;
}

在这里插入图片描述


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dhdhdhdhg

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值