C语言字符串函数内存函数(详解+实现)

字符串函数

长度不受限的字符串函数

strlen

strlen的作用就是求字符串长度,从开始位置一直计数到\0之前,返回的是\0之前出现的字符个数。

在这里插入图片描述

strlen返回的是无符号数,所以如果比较两个字符串长度的时候不可以用strlen相减,因为无符号数-无符号数最后还是无符号数。

int main()
{
    if(strlen("abc")-strlen("abcd")<0)
        printf("<");
    return 0;   
}

strcpy

字符串拷贝函数,函数原型为:
在这里插入图片描述

因为source是不会被修改的所以加上source修饰。
注意:这里的source必须是以\0结尾的,不然拷贝找不到\0停不下,就会越界
拷贝会连着src末尾的\0一起拷贝过去。
dest的空间必须足够大,可以容纳下src
dest的空间必须可以修改,不可以是常量字符串

strcat

字符串追加函数:
在这里插入图片描述

将src这个字符串追加到dest后面,从dest的\0开始,最后会连同src的\0一同追加过去。
这里注意,strcat不可以自己给自己追加,因为追加的第一个字符覆盖了\0,这样子src就找不到\0了,就会陷入死循环最后越界。
注意:
目标空间必须足够大可以容纳下两个字符串的内容,目标空间必须可以修改。
源字符串必须有\0,不然就会越界。

strcmp

在这里插入图片描述

比较两个字符串的大小,比较原理是,先比较第一个字符的ASCII码值,如果相同就比较第二个字符,直到两个字符串都到了\0,或者遇到不相等的字符,根据ascii码值,如果第一个字符串的这个字符的ASCII码值大就返回>0的数字,如果小就返回小于0,相等的时候会返回0.

长度受限的字符串函数:

上面介绍的字符串函数都是长度不受限制的字符串函数,下面这些都是长度受到限制的字符串函数。我们知道长度不受限制很可能会出现安全隐患,比如,拷贝和字符串连接的时候目标空间够。这时候可以用strncpy进行字节决定长度的拷贝或者连接。

strncpy

在这里插入图片描述

该函数的作用就是将源字符串的前num个字节拷贝到dest处,如果num大于src的长度,这时候会在拷贝完之后在dest后面追加\0直到数目达到num。

strncat

在这里插入图片描述

在dest后面连接num字节数个src的字符。如果src的长度小于num那么只连接到src的\0处(也即是全部将src追加过去),就相当于是strcat了。这个函数也和strcat一样都会在末尾补上\0。

strncmp

比较两个字符串的前n个字节的字符
在这里插入图片描述

这个就是比较两个字符串的前num个字符,直到遇到不相等的字符串或者是遇到字符串结束,或者是达到了num个。

strstr

查找子串
在这里插入图片描述

在str1中查找str2这个子字符串,如果找到了就返回这个子字符串在str1中的首地址,找不到就返回空指针。

strtok

字符串分割函数
在这里插入图片描述

del字符串保存了分割字符串,就是作为分隔符的字符的集合。
注意:这个函数分割字符串的时候会改变原来的字符串,所以一般是使用拷贝的字符串进行分割。所以str不可以是常量字符串。
该函数找到了分隔符之后会将分隔符置为\0,然后返回这个分隔符前的字符串的首地址。并且函数会保存在字符串中的位置。下次寻找分割符的时候直接从该位置向后查找。所以下一次查找的时候第一个参数就不需要了,只需要传空指针即可。

int main()
{
	char arr[] = "hello@world&kisskernel";
	char copy[40];
	strcpy(copy, arr);
	for (char* ret = strtok(copy, "@&"); ret != NULL; ret = strtok(NULL, "@&"))
	{
		printf("%s\n", ret);
	}
	return 0;
}

在这里插入图片描述

如果最后查找不到了就会返回NULL,这里如果多个分隔符连在一起比如@@或者@&&&,这种只有第一个分隔符有效剩下的忽略掉,因为这每两个分隔符之间没有字符串,所以也不会额外打印出来空行

strerror

在这里插入图片描述

这个函数就是返回错误码对应的错误信息。C语言中如果程序执行过程中遇到错误就会生成一个错误码,并且保存在全局变量errno中,所以只要引用头文件#include<errno.h>然后给strerror传参errno即可打印出来错误信息。

内存操作函数

上面的函数只能操作字符串,那么int,float等其他的类型的数组该怎么操作呢?这就用到了内存操作函数。

memcpy

内存拷贝函数:
在这里插入图片描述

将src的前num个字节拷贝到dest中最后返回的是dest的地址。该函数不管什么情况总是将src的num个字节拷贝到dest中,所以至少以保证dest的空间要是大于num的src的有效字节数也应该是大于等于num的。同时dest和src不可以是重叠的,如果重叠的拷贝使用memmove会更安全,因为如果内存重叠,memcpy默认从前向后拷贝就会将要拷贝过去的数据修改。
例如将:[1,2,3,4,5,6,7,8,9]将这个数组的1234拷贝到3456的位置,在拷贝12的时候已经将34改成了12,所以后面的拷贝会让56也变成12。

memmove

上面的memcpy不能拷贝发生重叠的数据,所以库函数提供了memmove来处理重叠的数据拷贝,在vs编译器里面memcpy可以拷贝重叠数据就是实现了memmove的功能,但是C语言标准规定memcpy只需要实现不重叠的内存拷贝即可。
在这里插入图片描述

memmove的使用方法是和memcpy一样的。

memcmp

内存比较函数,比较两块内存空间的内容,注意这里的比较方法是每个字节每个字节的比较。
在这里插入图片描述

如果两块内存空间相等就返回0,这个函数直到将所有num字节比较完,或者是遇到不相等的字节就停下来。
ptr1>ptr2的某个字节就返回>0的数字,ptr1<ptr2就返回<0的数字。

memset

在这里插入图片描述

内存设置函数,可以将ptr的num字节全部设置成为value,要注意设置int的时候如果value是1,并不是吧每个元素都初始化成1,而是这个元素的每个字节都是1。

字符串函数的模拟实现和注意

strlen模拟实现

这里实现strlen有三种方式
1,循环
2,递归(不创建变量)
3,指针-指针

size_t my_strlen(const char* str)
{
	assert(str);
	size_t count = 0;
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}

断言要注意,str不能是空指针所以需要断言。然后就让指针不等于\0不断向后移动,计数器++,最后返回计数器的值。

//递归
int my_strlen1(const char* str)
{
	assert(str);
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen1(str + 1);
}

//指针-指针
int my_strlen2(const char* str)
{
	assert(str);
	char* begin = str;
	while(*str!='\0')
	{ 
		str++;
	}
	return str - begin;
}

strcpy模拟实现

char* my_strcpy(char* des, const char* sur)
{
	assert(des && sur);
	char* ans = des;
	while (*des++ = *sur++)
	{}
	return ans;
}

这里的实现很巧妙,用while判断条件,先将sur的值拷贝过去,然后判断sur是不是为\0,不是\0就继续,是\0就会退出循环,因为\0的ASCII码值就是0,所以可以直接当作判断为假的条件。这样可以省去最后再补上\0这一句代码。

strcat模拟实现

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

思路就是,先找到dest的\0位置然后进行字符串拷贝。要连着src的\0一起拷贝过去。

strcmp模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2 && *str1!='\0')
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

思路就是循环比较两个字符串对应字符,相等就继续,直到字符串不相等或者str1遇到了\0就跳出循环。为什么str1遇到\0就跳出循环?str2遇到\0呢?这里写str1或者str2都是可以的。因为这里情况:如果str1先遇到\0,这时候str2不为\0,不相等,跳出循环,str2遇到\0也是这样。如果两个同时遇到\0这时候还是相等的,但是不可以继续++了需要第二个str1!=\0这个条件跳出循环。
最后返回两个的差值就可以了。str1大差值为+,str2大差值为-,str1str2\0就会返回0.

strstr模拟实现

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (*str2 == '\0')
		return str1;
	char* p = str1;

	while (*p)
	{
		char* s1 = p;
		char* s2 = str2;
		while (*s1 == *s2 && *s1!='\0')
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;
		p++;
	}
	return NULL;
}

思路:如果str2是空字符串直接返回str1,然后str2不为空的时候就进行比较
情况1:直接从前向后比较成功了。
情况2:就是部分字符相同,比较到后面不相同了。这时候就需要重新匹配从刚刚str1开始匹配的下一个字符开始。
所以使用了s1和s2两个指针,用来移动比较,p用来标记str1中的位置。如果s1和s2的字符相同则开始比较,如果比较到了s2为\0也即是比较完毕,完全匹配,就返回首字符的地址,也就是从p位置开始匹配的这个字符串首地址也就是p指针。如果不相等,就让p向后移动然后如果p没到str1的结尾就将p赋值给s1,s2也回到str2的开头再次进行比较。

memcpy模拟实现

void* my_memcpy(void* dest, const void* src, int size)
{
	assert(dest && src);
	void* ret = dest;
	while (size--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;
		++(char*)src;
	}
	return ret;
}

思路:就是将src和dest他们强转成为char*类型的指针一个字节一个字节的拷贝。

memmove模拟实现

关于memmove的内存重叠,我们可以发现如果是这种情况src是源头,dest是要拷贝过去的目的地
在这里插入图片描述

这时候需要从前——》后拷贝,如果从后向前就会先覆盖掉src还没有拷贝的内容
在这里插入图片描述

而这种情况则需要从后——》前拷贝,所以综合这两种情况,我们可以得出一个判断条件就是
dest>src的时候从后向前,dest<src从前向后,下面是实现代码:

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)//从前向后
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KissKernel

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

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

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

打赏作者

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

抵扣说明:

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

余额充值