字符串各操作函数与内存函数详解

strlen

首先第一位男嘉宾是我们的strlen函数

首先看一下函数原型:>

在这里插入图片描述

strlen函数的参数是要求的字符串的地址,返回值为size_t类型,此处要注意以下几个点:>

  1. strlen函数的返回值为size_t类型,即无符号整型,会出现以下这种情况:>
    在这里插入图片描述

if语句里的strlen(arr1) - strlen(arr2),即 3 - 4 = -1, 然而这是一个无符号整型 - 无符号整型的表达式,得到的仍然是无符号整型,-1将会被当作是一个很大的正数,因此这样的话答案就是>了
在这里插入图片描述

2.strlen所求的字符串中必须有\0, strlen是通过读取到\0来停止读取,因此若字符串中没有结束标志,其得到的结果只能为随机值

在这里插入图片描述

运行结果为:
在这里插入图片描述

接下来是strlen的模拟实现,有三种方式,这里我所写的是指针-指针的方式

int my_strlen(const char* start)
{
	assert(start);
	const char* end = start;
	while (*end != '\0')
	{
		end++;
	}
	return end - start;
}

strcpy

第二位出场的男嘉宾是我们的strcpy函数,其功能是将一个字符串拷贝到另一个字符串,即是这样:

在这里插入图片描述

将arr2拷贝放到arr1中,这样arr1数组存放的也是arr2中的内容,打印出来的结果为:>

在这里插入图片描述

其次要注意的几个点是

1.拷贝的源字符串中必须有\0, strcpy同样是以\0作为拷贝的结束标志。

2.拷贝的时候,也会将\0拷贝过去。

下面通过调试来展示一下

现在,我们将第一个数组的字符串内容改变,即

char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
char arr2[] = "hello, world!";
strcpy(arr1, arr2);
printf("%s\n", arr1);

在经过strcpy后

在这里插入图片描述

可以看出,\0被拷贝了过去。

接下来是strcpy的模拟实现:

char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

*dest++ = *src++这句代码既解决了拷贝问题,同时也将’\0’拷贝了过去。

strcmp

strcmp是我们常用的字符串比较函数,用来比较两字符串之间的大小关系

例如:>

int main()
{
	char arr1[] = "LLLLLL";
	char arr2[] = "LLLYYY";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

strcmp将每个字符依次比较,在此处, arr1中的L先于arr2中的L比较,一直比较到arr1中的第四个L与arr2中的第一个Y, 此时发现字符L的ascii码值小于字符’Y’,返回一个小于0的数,结果为:>
在这里插入图片描述

若我们将代码修改一下,若将arr2中的内容修改为:>

int main()
{
	char* arr1 = "LLLLLL";
	char* arr2 = "LLLLLL";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

则得到的结果为:>

在这里插入图片描述

接下来是strcmp的模拟实现:>

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

此处的return *s1 - *s2也可以修改为以下代码:>

if(*s1 > *s2)
{
    return 1;
}
else
{
    return -1;
}

strcat

接下来是我们的字符串追加函数,首先,其功能为在字符串后面追加字符串,以一串代码来演示以下:>

int main()
{
	char arr1[20] = "LLLLLL";
	char arr2[] = "YYYYYY";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

得到的结果为:>

在这里插入图片描述

要注意的点是:>

  1. 被追加的字符串函数中必须有足够大的空间,不然会导致非法访问内存;
  2. 追加的字符串同时会把’\0’复制过去。

然后就是strcat的模拟实现啦



#include <assert.h>

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest)
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = "LLLLLL";
	char arr2[] = "YYYYYY";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

接下来的三组函数与上面三组类似,分别是strncpy, strncmp, strncat。

首先是我们的strncpy同学,与strcpy相比多了一个参数

在这里插入图片描述

此处的第三个参数指明的是要复制多少个字符过去。

接下来是其功能展示:

int main()
{
	char arr1[20] = "LLLLLLL";
	char arr2[] = "YYYYYYYY";
	strncpy(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

得到的结果为:>

在这里插入图片描述

我们修改一下代码,改为以下代码:>

int main()
{
	char arr1[] = "xxxxxxx";
	char arr2[] = "YYY";
	strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

此处第三个参数‘5’已经超过了arr2中字符串的长度,arr1中得到的结果为:>

在这里插入图片描述

可以看到,strncpy不仅把arr2中的内容拷贝过去,又在其后面补了个’\0’以此到达第三个参数所要的复制五个字符。

接下来是我自己对strncpy的模拟实现,欢迎各位大佬提出意见!

char* my_strncpy(char* dest, const char* src, size_t count)
{
	char* ret = dest;
	while (count && (*dest++ = *src++)) // 这里表达式(*dest++ = *src++)必须放在&&的后面,当count为0时
	{									//表达式(*dest++ = *src++)才不计算,防止多算一次
		count--;
	}

	while (count)
	{
		*dest++ = '\0';
		count--;
	}
	return ret;
}

接下来是我们的strncat,与strcat相似,但同样是多了一个参数

在这里插入图片描述

第三个参数同样是指明在字符串后面要多追加几个数。接下来我们看其功能


int main()
{
	char arr1[20] = "xxxxxxx";
	char arr2[] = "YYYYYYY";
	strncat(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

得到的结果为:>

在这里插入图片描述

可以看出,strncat不仅追加了五个字符,同时还在其末尾追加了’\0’,如果arr2中的字符串长度小于第三个参数所指定的长度呢,请看以下代码:>

int main()
{
	char arr1[20] = "xx\0xxxxxxxxx";
	char arr2[] = "YYY";
	strncat(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

我们以调试的方式来看一下在执行这几行代码之后所得到的结果:>

在这里插入图片描述

我们发现,在把arr2中的所有字符追加过去后,即使arr2的字符串长度小于strncat第三个参数所指明的参数,仍然只追加了arr2中的内容,并不会像strncpy那样多放几个’\0’过去。

接下来是自己的strncpy的模拟实现,欢迎各位大佬提出意见!

char* my_strncat(char* s1, const char* s2, size_t count)
{
	char* tmp = s1;
	size_t len = strlen(s2);
	while (*s1)
	{
		s1++;
	}
	if (count < len)
	{
		while (count && (*s1++ = *s2++)) 
		{									
			count--;
		}
		*s1 = '\0';
	}
	else
	{
		while (len && (*s1++ = *s2++))
		{
			len--;
		}
		*s1 = '\0';
	}
	return tmp;
}

这三组函数中最后一组函数便是我们的strncmp函数了,首先同理,既然名字中多了一个n,那么他也同样多了一个参数。

在这里插入图片描述

首先我们来看其功能:>

在这里插入图片描述

这里的第三个参数是为了指明要对比两个字符串中的前多少个字符,这里传参传的是4,因此对比arr1和arr2中的前四个字符,得到的结果为:>

在这里插入图片描述

若将此处的第三个参数改为5的话,得到的结果是:>

在这里插入图片描述

因为arr1的第五个字符’L’与arr2中的第五个字符’Y’并不相等,因此返回小于0的数。

接下来是strncmp自己的模拟实现,欢迎各位大佬提出意见:>

int my_strcmp(const char* s1, const char* s2, size_t count)
{
	assert(s1 && s2);
	int i = 0;
	for (i = 0; i < count; i++)
	{
		if (*s1 == *s2)
		{
			s1++;
			s2++;
		}
		else
		{
			return *s1 - *s2;
		}
	}
	return 0;
}

接下来出场的是:

strstr

即字符串查找函数,找一个字符串是否为另一个字符串的子串。若找到,则返回找到该字串的地址,若找不到,则返回空指针。请看以下范例:>

int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
	{
		return 0;
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

得到的结果为:>

在这里插入图片描述

接下来是strstr的模拟实现(kmp算法我还没了解。。。。)

char* my_strstr(const char* str, const char* substr)
{
	assert(str && substr);
	if (*substr == '\0')
	{
		return (char*)str;
	}
	const char* s1 = str;
	const char* s2 = substr;
	const char* cur = str;
	while (*cur)
	{
		s1 = cur;
		s2 = substr;
		while (*s1 && *s2 && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cur;
		}
		cur++;
	}
	return NULL;
}

接下来是4组内存函数,分别是

memcpy;
memmove;
memset;
memcmp;

首先登场的是memcpy,我们来看其参数
在这里插入图片描述

第一个参数是待拷贝的字符串,第二个参数是拷贝的字符串,第三个是拷贝多少个字节,请看以下范例:>

int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = { 0 };
	memcpy(arr2, arr1, 5);
	printf("%s\n", arr2);
	return 0;
}

此处代码便是将arr1前五个字节的内容拷贝到arr2中。得到的结果为:>
在这里插入图片描述

此外,memcpy函数还可以操作其他类型的数组

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

此处代码便是将arr1中的前20个字节的元素拷贝到arr2中,得到的结果为:>

在这里插入图片描述

接下来便是memcpy的模拟实现啦:>

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

接下来登场的是我们的memmove函数,不过memmove函数与memcpy函数非常相似,但memmove函数可以处理重叠问题。(其实vs的memcpy也可以),请看一下代码:>

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


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

此处我们是想将 1 2 3 4 拷贝到3 4 5 6 本想得到的结果为:1 2 1 2 3 4 7 8 9 10,然而我们得到的结果为:>

在这里插入图片描述

此处memmove就可以很好的解决问题,我们修改一下代码:>


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

得到的结果为:>
在这里插入图片描述

memmove便可以解决这些重叠的问题,接下来我们重点观看一下memmove的模拟实现:>

在这里插入图片描述

首先请看这幅图,如果将1,2,3,4拷贝到3,4,5,6会覆盖3,4的话,我们可以尝试着倒过来拷贝,即
在这里插入图片描述

将4拷贝到6,将3拷贝到5,将2拷贝到4,将1拷贝到3,这样就得到我们想要的结果了。

再来看一种情况

在这里插入图片描述

这种情况下正着拷贝就可以了,即将3拷贝到1,4拷贝到2,5拷贝到3,6拷贝到4.

最后一种情况:>

在这里插入图片描述

两者都可以为src或者dest,这样的话,倒着打印或者正着打印就可以了,因此,我们可以分为两种情况

在这里插入图片描述

有这些知识之后,接下来是memmove的模拟实现:>

void* my_memmove(void* dest, const void* src, size_t count)
{
	void* tmp = dest;
	assert(dest && src);
	if (src > dest)
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}
	return tmp;
}

memcmp

接下来是内存比较函数memcmp,即比较每个字节的内容是否相等,与strcmp挺相似的
在这里插入图片描述

请看以下代码


int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,6,7,8,9,10,11 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);
	return 0;
}

运行结果为:>

在这里插入图片描述

将16改为17的话,得到的结果为:>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,6,7,8,9,10,11 };
	int ret = memcmp(arr1, arr2, 17);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

此处返回-1是因为arr1和arr2中第17个字节的内容是不同的,且arr1中的内容小于arr2.

memset

最后出场的是我们的memset函数,即内存设置函数,我们来看其演示效果:>

int main()
{
	char arr1[] = "LLLLLLLL";
	memset(arr1, 'x', 5);
	printf("%s\n", arr1);
	return 0;
}

memset即将arr1中前5个字节(在此处为5个字节,由第三个参数决定)设置为’x’字符x,得到的结果为:>

在这里插入图片描述

本文完!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值