关于一些字符串相关函数,内存函数及部分模拟

1  字符串函数

 首先是长度无限制的strcpy,strcmp,strcat;

这几个函数从左至右功能依次是  字符串复制,字符串比较,字符串追加,

但是这几个字符串函数并不是很安全,因为有可能接受的字符串的大小不够,因此又有一种字符串有限制的函数

strncpy,strncmp,stcncat;

这几个函数会限制只能复制,比较或者追加前n个字符,相对安全。

重点来啦:strstr函数

strstr函数的作用是查找子串,比如在 "abbbcdedf"中查找"bbc"是否存在,并且返回匹配的位置。

接下来是模拟strstr写出的my_strstr函数

代码如下:


#include<stdio.h>

const char* my_strstr(const char* str1,const char* str2)
{
	char* p1 = str1;
	char* p2 = str2;
	while (*p1)
	{
		
		str1 = p1;
		str2 = p2;
		while (*str1 == *str2)
		{
			str1++;
			str2++;
			if (*str2 == '\0')
				return p1;//返回匹配开始的位置
		}
		p1++;
	}
	return NULL;//若是p1都是'\0'则表示匹配失败,输出NULL;
}

int main()
{
	char s[] = "abbbcddef";
	char d[] = "bbc";
	char e[] = "qeww";
	char* ret = my_strstr(s, d);
	printf("%s\n", ret);
	ret = my_strstr(s, e);
	printf("%s", ret);
	return 0;
}

 

我们先后将s字符串先后和d字符串,e字符串比较,发现d字符串和s字符串是能够匹配的,并且返回了s字符串和d字符串匹配的位置,而e字符串则不能和s字符串匹配,因此返回null;

重点2  strtok函数

strtok函数能够切割字符串

具体示例如下:


#include<stdio.h>
#include<string.h>
int main()
{
	const char* sep = "@.";
	char email[] = "3327618203@qq.com";
	char str[20];
	strcpy(str, email);
	for (char* ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	printf("%s", email);
	return 0;
}

 这个函数会在email字符串中依次寻找有没有和sep里面对应的字符,若是有,则在email字符串中改为'\0',因为这个函数会对email进行修改,因此此处我先将email的内容复制在strcpy中再进行分割,表现如下;

 可以看到,分割后分别打印出了str的三部分字符串,使用这个函数有一个要注意的就是,只有第一次需要传str函数的数组名,后面只用传NULL就可以了,因为每次这个函数都会在str字符串中有分隔符号的位置留下一个标记在堆区中,而这个标记就是NULL,因此只有第一次使用需要传字符串首元素地址作为传参,后面只用传NULL,编译器自己会根据这个标记找到对应的修改位置。

内存函数

前面讲过strcpy是用来复制字符串的函数,那么有没有一种函数什么数组都能够复制呢?答案是肯定的,比如接下来的memcpy,顾名思义,这函数就是用来复制内存的,我们都知道数据在编译器中是以字节为单位进行存储的,而这个函数就是一个一个字节复制,因此无论什么类型都能够复制,下面是我对这个函数的模拟

memcpy函数模拟 

#include<stdio.h>

void* my_memcpy(void* ret, const void* src, size_t num)
{
	void* p = ret;
	while (num--)
	{
		*(char*)ret = *(char*)src;
		ret = (char*)ret + 1;
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[5] = { 0 };
	char c[20] = "abcdef";
	char d[20] = { 0 };
	my_memcpy(b, a,20);
	my_memcpy(d, c,6);
	for (int i = 0; i < 5; i++)
		printf("%d ", b[i]);
	printf("\n%s", d);

	return 0;
}

 

我们可以看到这个函数不管是int类型的数组还是char类型的数组都能够使用,那么我们来深究一下代码:

void* my_memcpy(void* ret, const void* src, size_t num)
{
	void* p = ret;
	while (num--)
	{
		*(char*)ret = *(char*)src;
		ret = (char*)ret + 1;
		src = (char*)src + 1;
	}
	return ret;
}

这个函数一共有三个形参,ret表示用来接受数据的数组,src表示有数据的数组,而num则表示需要复制的内存的字节数,但是前两个为什么都是void*和const void * 类型呢?

由于这个函数是不管什么类型都能够复制的,因此我们接受的形参应该使用void * 类型的,因为这个类型是万能类型,无论什么都能接受,但是为什么我们在复制的时候,会使用*(char *)ret = *(char *)src呢?为什么一定要将他们强制类型转化为char* 类型之后才解引用呢?这是因为char *类型是一个字节,只有这样,我们才能逐个字节的复制保证自己不出错。

讲完memcpy我们再来讲讲memmove函数。

memmove函数

这个函数实际上和memcpy函数差不多,但是这个函数是专门用来实现内存重叠拷贝的,因为memcpy若是对于一个数组内部进行拷贝,会发生错误,而这个函数就不会出现这种状况。

函数模拟 


#include<stdio.h>
#include<assert.h>
void* my_memmove(void* des, const void * src, size_t num)
{
	assert(des && src);
	void* ret = des;
	if (des > src)
	{
		//
		while (num--)
		{
			*((char*)des + num) = *((char*)src + num);
		}
	}
	else
	{
		while (num--)
		{
			*(char*)des = *(char*)src;
			des = (char*)des + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

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

	return 0;
}

 这个函数的形参和memcpy类似,而代码实现形式却不一样,这个会根据des和src在内存的前后顺序不同来决定是从前向后复制还是从后向前复制,具体实现就是上面。

示例如下: 

 

这就是本人新学的几个小函数,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值