字符串函数+内存函数(详解)

1.引言

本期带大家一起来学习字符串函数+内存函数😀 😃 😄

一、字符串函数📌📌

1. 求字符串长度 strlen🚀🚀

字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。

#include<stdio.h>
#include<string.h>
int main()
{
	char* p = "abcdef";
	size_t len = strlen(p);
	printf("len=%zd\n", len);
	return 0;
}


strlen在库里面的参数是如此的⌛️⌛️
接下来我们来模拟实现strlen,接下来使用三种方法求字符串长度

//方法一     使用循环求字符串长度
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* p)
{
	assert(p);
	size_t len = 0;
	while (*p != '\0')
	{
		p++;
		len++;
	}
	return len;
}
int main()
{
	char* p = "abcdef";
	size_t len = my_strlen(p);
	printf("len=%zd\n", len);
	return 0;
}


//方法二         递归求字符串长度
#include<stdio.h>
#include<assert.h>
unsigned int mystrlen(const char* p)
{
	assert(p);
	if (*p == '\0')
		return 0;
	return 1 + mystrlen(p + 1);
}
int main()
{
	char arr[] = "abcdefghi";
	unsigned int ret = mystrlen(arr);
	printf("%u", ret);
	return 0;
}


//方法三       运用指针求字符串长度
#include<stdio.h>
#include<assert.h>
unsigned int mystrlen(const char* p)
{
	assert(p);
	char* start = p;
	while (*p != '\0')
	{
		p++;
	}
	return p - start;

}
int main()
{
	char arr[] = "abcdefghi";
	unsigned int ret = mystrlen(arr);
	printf("%u", ret);
	return 0;
}

注意点:
字符串是以’\0’作为结束的标志,strlen是统计字符串’\0’之前的字符。
参数指向的字符串必须要以 ‘\0’ 结束
字符串的返回类型是size_t类型。

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

2.1 strcpy🌴🌴

这里是引用
strcpy在库里面的参数是如此的
接下来我们来模拟实现strcpy

#include<stdio.h>
#include<assert.h>
char* mystrcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest++ = *src++)
	{
		;
	}
	return ret;

}
int main()
{
	char arr[20] = "abcdef";
	char str[20] = "hello world";
	char *ret=mystrcpy(arr, str);
	printf("%s\n", ret);
	return 0;
}

strcpy函数注意点:⌛️⌛️
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。🔑🔑

2.2 strcat🌴🌴

在这里插入图片描述

strcat是字符串追加函数,能够将源字符串的字符追加到目标字符串中。
💬💬 strcpy函数注意点:🔑🔑🔑
返回类型是char*(返回的是目标字符串首的地址)
接下来模拟实现strcat

#include<stdio.h>
#include<assert.h>
char* mystrcat(char* dest, const char* src)
{
 assert(dest&&src);
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr[20] = "hello ";
	char* p = "abcdef";
	char str[20] = "world";
	char* ret = mystrcat(arr, str);
	printf("%s\n", ret);
	return 0;
}

注意点:strcat函数💬💬
源字符串 必须以’\0’ 结束
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
不能自己给自己追加字符串,程序会崩溃❗️❗️❗️

📣📣当我们自己给自己追加的时候,会出现死循环,接下来看图解📣📣
在这里插入图片描述

2.3 strcmp🌴🌴

在这里插入图片描述

strcmp是字符串比较函数,该函数是从二个字符串的元素开始🚩🚩
进行比较 (比较本质为字母ascii码值的大小)
接下来模拟实现strcmp

#include<stdio.h>
#include<assert.h>
int mystrcmp(const char* p,const char * q)
{
	assert(p && q);
	while (*p == *q)
	{
		p++;
		q++;
		if (p == '\0')
			return 0;
	}
	return *p - *q;
}
int main()
{
	char* p = "abcdef";
	char* q = "abcccc";
	int ret = mystrcmp(p, q);
	printf("%d\n", ret);
	return 0;
}

标准规定: strcmp函数
第一个字符串大于第二个字符串, 则返回大于0的数字🌈
第一个字符串等于第二个字符串,则返回0🌈🌈
第一个字符串小于第二个字符串,则则返回小于0的数字🌈🌈🌈

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

上面的 strcmp,strcpy,strcat都是长度不限制字符串函数,具有一定的风险性,这和VS当中scanf函数一样,具有一定的风险,所以下面来介绍长度受限制的字符串函数 strncmp,strncpy,strncat

3.1 strncpy⚓️⚓️

在这里插入图片描述

这是strncpy的参数,前两个参数和 strcpy是一样的,不同的是第三个参数
第三个参数则是传入需要复制的 字符 个数
在这里插入图片描述

3.2 strncat⚓️⚓️

在这里插入图片描述

这是strncat的参数,前两个参数和 strcat是一样的,不同的是第三个参数
第三个参数则是传入需要连接第二个参数 src 字符 个数
在这里插入图片描述
当我们的传入的 第三个参数小于字符串长度
后面会自动补 斜杠0
在这里插入图片描述

3.3 strncmp⚓️⚓️

在这里插入图片描述

这是strncmp的参数,前两个参数和 strcmpt是一样的,不同的是第三个参数
第三个参数则是传入需要比较的dest 和 src 字符 个数
在这里插入图片描述
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完
当需要比较的字符个数小于实际的字符串长度
会提前结束
在这里插入图片描述

4.字符串查找💡💡

4.1 strstr🌱🌱

在这里插入图片描述

这是strstr的参数✔️✔️
返回的是在被查找字符串当中与查找字符串相同的首元素的地址
第一个参数是被查找字符串的首元素的地址
第二个参数是查找字符串的首元素地址
在这里插入图片描述

接下来我们来模拟实现strstr
方法一 :暴力求解🌴
方法二 :借助strncmp求解🌴🌴
方法三 :KMP算法求解🌴🌴🌴

//方法一:暴力求解
#include<stdio.h>
#include<assert.h>
char* mystrstr(const char* dest, const char* src)
{
	assert(dest && src);
	char* start = dest;
	while (*start)
	{
		char* p = start;
		char* q = src;
		while (*p == *q && *p && *q )
		{
			p++;
			q++;
		}
		if (*q == '\0')
			return start;
		start++;
	}
	return NULL;
}
int main()
{
	char* p = "abcdededefabc";
	char* q = "def";
	char* ret = mystrstr(p, q);
	printf("%s\n", ret);
	return 0;
}
//方法二:借助strncmp求解
#include<stdio.h>
#include<assert.h>
#include<string.h>
char* mystrstr(const char* dest, const char* src)
{
	assert(dest && src);
	char* p = dest;
	while (*p)
	{
		if (strncmp(p, src, strlen(src)) == 0)
			return p;
		p++;
	}
	return NULL;
}
int main()
{
	char* p = "abcdededefabc";
	char* q = "def";
	char* ret = mystrstr(p, q);
	printf("%s\n", ret);
	return 0;
}

//方法三:KMP算法实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
void my_next(int* next,int n,const char* p)
{
	int j = 0,k=-1;
	next[0] = -1;
	while(j<n)
	{
		if (k == -1 || p[j] == p[k])
		{
			next[j + 1] = k + 1;
			j++;
			k++;
		}
		else
			k = next[k];
	}
}
int KMP(const char* str1, const char* str2)
{
	int i = 0, j = 0;
	int len = (int)strlen(str2);
	//next数组
	int* next = (int*)malloc(len * sizeof(int));
	assert(next);
	my_next(next,len-1,str2);
	while (str2[j])
	{
		if(j==-1||str1[i] == str2[j])
		//j为-1时该位置下的i不会匹配成功,进入下一次匹配
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//j进行回退
		}
		if (str1[i] == '\0')
		{
			free(next);
			next = NULL;
			return -1;
		}
	}
	free(next);
	next = NULL;
	return i;
}
int main()
{
	char arr[] = "abaabcdabcab";
	char brr[] = "ef";
	printf("%d\n",KMP(arr, brr));
	return 0;
}

4.2 strtok🌱🌱

在这里插入图片描述

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

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[20] = "abc@dfg#hi.123";
	char* p = "@#.";
	char str[30] = "0";
	strcpy(str, arr);
	char* ret = NULL;
	for (ret = strtok(str, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
	return 0;
}

在这里插入图片描述

5.错误信息报告 strerror🌱🌱

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	return 0;
}

🍗🍗 返回错误码,所对应的错误信息🍗🍗
返回的是错误信息

二、内存操作函数📌📌

1.memcpy🚁🚁

在这里插入图片描述
函数memcpy从source的位置开始向后
复制num个字节的数据到destination的内存位置。✏️✏️
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
🔑 💡🔑 💡

接下来我们来模拟实现一下memcpy

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		
		*((char*)dest) = *((char*)src);
		dest = ((char*)dest + 1);
		src = ((char*)src + 1);

	}
	return ret;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int *ret=my_memcpy(arr, arr + 2,5 * sizeof(int));
	int len = sizeof(arr) / sizeof(int);
	while (len--)
	{
		printf("%d ", *ret);
		ret++;
	}
	return 0;
}

🔍🔍my_memcpy的参数是void * ,🔎🔎
void*指针可以接收任何类型的指针,这样子可以接收任何类型的指针,
🔍🔍 并且用将其强制类型转换为char *指针,🔎🔎
每次拷贝一个字节的数据,这样子方便使用并且加强了代码的可执行性

但是我们可以发现,当我们传参到我们的my_memcpy当中,
第一个参数的地址比第二个参数的地址高的时候
会出先重复的情况
❗️❗️❗️接下来看图解❗️❗️❗️
在这里插入图片描述
所以接下来我们来学习一下memmove函数,可以解决这个情况

2.memmove🚁🚁

在这里插入图片描述

和memcpy的差别就是memmove函数
处理的源内存块和目标内存块是可以重叠的。

如果源空间和目标空间出现重叠,就得使用memmove函数处理。
接下来我们来模拟实现一下memmove⭐️⭐️⭐️

#include<stdio.h>
#include<assert.h>
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;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr, arr + 2, 5 * sizeof(int));
	my_memmove(arr+2, arr , 5 * sizeof(int));
	char string1[60] = "abcdef123456";
	my_memmove(string1 + 3, string1, 3);
	char string2[60] = "abcdef123456";
	printf("string1=%s\n", string1);

	my_memmove(string2 , string2+3, 3);
	printf("\nstring2=%s\n", string2);
	return 0;
}

3.memset🚁🚁

在这里插入图片描述
在这里插入图片描述

memset是计算机中C/C++语言初始化函数。
作用是将某一块内存中的内容全部设置为指定的值,
这个函数通常为新申请的内存做初始化工作。
可以注意到第三个参数是需要改变字节的数目
⭐️⭐️⭐️接下来模拟实现memset⭐️⭐️⭐️


#include<stdio.h>
#include<string.h>
void* my_memset(void* dest,int c, size_t count)
{
	while (count--)
	{
		*(char*)dest = (char)c;
		dest = (char*)dest + 1;
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memset(arr, 0,sizeof(int) * 10);
	char str[20] = "abcdef";
	my_memset(str,'a', sizeof(char) * strlen(str));
	printf("%s", str);
	return 0;
}

在这里插入图片描述

但是当我们将第二个参数传入1的时候,结果会和我们预想的不一样,这是为什么呢?⁉️⁉️⁉️
因为我们开头提到memset是一个字节一个字节初始化的

1
在内存当中 小端 存储方式如下

01 00 00 00

在这里插入图片描述

强制类型转换为char后

是01
在这里插入图片描述

所以得出来的结果是⭕️ ⭕️ ⭕️

在这里插入图片描述

4.memcmp🚁🚁

在这里插入图片描述

在这里插入图片描述

memcmp函数和strcmp函数是差不多的🚦
区别在于memcmp函数可以比较任何类型的数据 🚦🚦
而strcmp函数只能比较字符串 🚦🚦🚦

三、感谢与交流📌📌

🌹🌹🌹如果大家通过本篇博客收获了,对字符串函数以及内存操作函数 ,那么希望支持一下哦如果还有不明白的,疑惑的话,或者什么比较好的建议的话,可以发到评论区,
我们一起解决,共同进步 ❗️❗️❗️
最后谢谢大家❗️❗️❗️💯💯💯

在这里插入图片描述

评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值