字符串与内存操作函数详解与模拟实现

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
个人主页:@rivencode的个人主页
系列专栏:玩转C语言
推荐一款模拟面试、刷题神器,从基础到大厂面试题点击跳转刷题网站进行注册学习

目录

  • 一.字符串数组与常量字符串的区别
  • 二.字符串操作函数
    • 1.strlen
    • 2.strcpy
    • 3.strcat
    • 4.strcmp
    • 5.strncpy
    • 6.strncat
    • 7.strncmp
    • 8.strstr
    • 9.strtok
  • 三.内存操作函数
    • 1.memcpy
    • 2.memmove
    • 3.memcmp

一.字符串数组与常量字符串的区别

const char * str=“abcdef” 与 char buffer[]="abcdef"的区别
先说结论:

字符串常量是存放在字符常量区与全局变量一样生命周期是从程序运行到结束,而且字符串常量是不可被修改。

单独一串 “abcdef” 就代表a字符的地址
字符串与内存操作函数详解与模拟实现_第1张图片

char buffer[]=“abcdef” 字符串被保存在数组中, 而数组又是在栈上开辟(局部数组),函数运行完时数组被销毁字符串也被销毁,保存在数组中的字符串可以被修改。

二.字符串操作函数

1.strlen

在这里插入图片描述
1.字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。
3.注意函数的返回值为size_t,是无符号的

1)字符串必须以’\0’结尾因为strlen只有找到’\0’才会停止计数

字符串与内存操作函数详解与模拟实现_第2张图片
字符串与内存操作函数详解与模拟实现_第3张图片
因为arr1数组没有存储\0字符所以strlen函数必须找到\0才停止计数,而数组后面的存储的数据是随机值,所以随机找到\0

2).注意函数的返回值为size_t,是无符号的
字符串与内存操作函数详解与模拟实现_第4张图片
字符串与内存操作函数详解与模拟实现_第5张图片
字符串与内存操作函数详解与模拟实现_第6张图片
如果对整形如果在内存中存储还不熟悉的请看——>C语言深度解剖之数据到底在内存中如何存储

strlen函数的三种模拟实现方式

  • 1.计数器
int my_strlen_1(const char* str)
{
	assert(str != NULL);
	int count = 0;
	while (*str++ != '\0')
	{
		count++;
	}
	return count;
}

返回值设为int就是为了避免相减产生歧义

  • 2.递归

int my_strlen_2(const char* str)
{
	assert(str != NULL);
	if (*str != '\0')
	{
		return 1 + my_strlen_2(str+1);
	}
	else
	{
		return 0;
	}
}
  • 3.指针减指针

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

2.strcpy

字符串与内存操作函数详解与模拟实现_第7张图片
字符串与内存操作函数详解与模拟实现_第8张图片
总结:
1.源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变,不能是一个字符串常量

1)目标空间必须足够大,以确保能存放源字符串。
字符串与内存操作函数详解与模拟实现_第9张图片

2)目标空间必须可变,不能是一个字符串常量
字符串与内存操作函数详解与模拟实现_第10张图片
strcpy函数模拟实现

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

源字符串中的 \0字符也要拷贝到目的字符串中

3.strcat

在这里插入图片描述
字符串与内存操作函数详解与模拟实现_第11张图片
总结:
1.源字符串必须以 ‘\0’ 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。
4.目的字符串与源字符串不应重叠(不能自己给自己追加)
5.目标字符串中的\0字符会被源字符串的第一个字符覆盖并且将源字符串中的\0字符拷贝到目标字符串

1)目的字符串与源字符串不应重叠(不能自己给自己追加)
字符串与内存操作函数详解与模拟实现_第12张图片
字符串与内存操作函数详解与模拟实现_第13张图片

strcat函数的模拟实现

char *my_strcat(char *dest,const char *src)
{
	assert(dest != NULL);
	assert(src   != NULL);
	char *ret = dest;
	//找到目的字符串的\0字符
	while (*++dest != '\0')
	{
		;
	}
	//追加字符串包含\0字符
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

4.strcmp

字符串与内存操作函数详解与模拟实现_第14张图片

总结:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

将两个字符串的字符一个个进行比较,如果有字符不同则比较这两个字符ASCLL码值,谁的ASCLL码值大则该字符串大,若比较到\0则两个字符串相等返回0.

字符串与内存操作函数详解与模拟实现_第15张图片
字符串与内存操作函数详解与模拟实现_第16张图片
字符串与内存操作函数详解与模拟实现_第17张图片
strcmp函数的模拟实现

int my_strcmp(const char * str1, const char *str2)
{
	assert(str1 != NULL);
	assert(str2 != NULL);
	while (*str1 == *str2)
	{
		if (*str1 == '\0' )
		{
			//比较到\0则两个字符串相等
			return 0;
		}
		str1++;
		str2++;
	}
	//有字符不相等,返回字符的ascll差值
	return *str1 - *str2;
}

5.strncpy

字符串与内存操作函数详解与模拟实现_第18张图片
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

字符串与内存操作函数详解与模拟实现_第19张图片
strncpy函数的模拟实现

char *my_strncpy(char *dest,const char* src, int count)
{
	assert(dest != NULL);
	assert(src != NULL);
	char * ret = dest;
	while ( count)
	{
		count--;
		if ((*dest++ = *src++) == '\0')
		{
			break;
		}
	}
	//如果要拷贝的字符数大于源字符串的大小,则后面补\0
	if (count)
	{
		while (count--)
		{
			*dest++ = '\0';
		}
	}
	return ret;
}

6.strncat

字符串与内存操作函数详解与模拟实现_第20张图片
总结:
1.目的字符串数组要足够大
2.若num小于源字符串的大小,追加num个字符后自动在后面追加一个\0
3.若num大于源字符串的大小,只会追加一个源字符串(包含\0)到目的字符串,后面不会继续追加\0.

1)若num小于源字符串的大小,追加num个字符后自动在后面追加一个\0
字符串与内存操作函数详解与模拟实现_第21张图片
strncat函数的模拟实现

char *my_strncat(char *dest, const char *src, int count)
{
	assert(dest != NULL);
	assert(src != NULL);
	char *ret = dest;
	while (*++dest)
	{
		;
	}
	while (count--)
	{
		if (!(*dest++ = *src++))
			return ret;
	}
	//若num小于源字符串的大小,追加num个字符后自动在后面追加一个\0
	 *dest = '\0';
	 return ret;
}

7.strncmp

字符串与内存操作函数详解与模拟实现_第22张图片
比较C字符串str1和C字符串str2的最多num个字符。
这个函数开始比较每个字符串的第一个字符。 如果它们彼此相等,则继续执行以下两对,直到字符不相同,直到达到终止的空字符,或直到两个字符串中有num个字符匹配,以先发生的情况为准。

strncmp函数的模拟实现

int my_strncmp(const char * str1, const char *str2, int count)
{
	assert(str1 != NULL);
	assert(str2 != NULL);
	while (count--)
	{
		if (*str1 != *str2)
		{
			break;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

8.strstr

字符串与内存操作函数详解与模拟实现_第23张图片
总结:
1.返回str1中第一个出现srt2的指针,如果str2不是str1的一部分,则返回空指针

strstr函数模拟实现

 char *my_strstr(const char*p1, const char* p2)
{
	assert(p1 !=NULL);
	assert(p1 !=NULL);
	const char *s1 = NULL;
	const char *s2 = NULL;
	const char *cur = p1;
	if (*p2 == '\0')
	{
		return NULL;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = p2;
		while ((*s1 == *s2) && (*s1 != '\0') && (*s2 != '\0'))
		{
			s1++;
			s2++;
		}
		//匹配成功
		if (*s2 == '\0')
		{
			return (char *)cur;
		}
		//匹配失败,提前结束
		if (*s1 == '\0')
		{
			return NULL;
		}
		cur++;
	}
	return NULL;
}

实现思路:
创建两个临时指针来进行字符的比较,而不改变原来两个在字符串的首地址,因为一旦第一趟比较失败,第二个字符串要从首字符进行比较,而第一个需要从下一个字符进行比较,所以p1指针与p2指针最好不动。

字符串与内存操作函数详解与模拟实现_第24张图片
字符串与内存操作函数详解与模拟实现_第25张图片

9.strtok

在这里插入图片描述

1.delimiters参数是个字符串,定义了用作分隔符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回 NULL 指针。

字符串与内存操作函数详解与模拟实现_第26张图片
字符串与内存操作函数详解与模拟实现_第27张图片

三.内存操作函数

前面的字符串操作函数只能是针对字符串,而如果要拷贝,追加其他类型的元素,字符串操作函数就不适用啦,所以我们需要一组内存操作函数直接拷贝或追加、比较,内存中的数据,以数据类型无关。

1.memcpy

==字符串与内存操作函数详解与模拟实现_第28张图片
总结:
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\0’ 的时候并不会停下来,就是复制num个字节数据
3.如果source和destination有任何的重叠,复制的结果都是未定义的。

字符串与内存操作函数详解与模拟实现_第29张图片
将arr1的内容拷贝到arr2,只需要知道arr1占内存多少个字节,然后拷贝多少个字符到arr2数组中

memcpy的模拟实现

 void *my_memcpy(void *dest, const void *src, size_t num)
 {
	 assert(dest != NULL);
	 assert(src   != NULL);
	 void *ret = dest;
	 while (num--)
	 {
		 //一个一个字节进行拷贝
		 *(char*)dest = *(char*)src;
		 ++(char*)dest;
		 ++(char*)src;
	 }
	 return ret;
 }

字符串与内存操作函数详解与模拟实现_第30张图片

1)如果source和destination有任何的重叠,复制的结果都是未定义的
字符串与内存操作函数详解与模拟实现_第31张图片
字符串与内存操作函数详解与模拟实现_第32张图片
如果source和destination有重叠,这个是实现的是从前向后拷贝所以可能会出问题

字符串与内存操作函数详解与模拟实现_第33张图片
所以得不同的重叠拷贝需要不同的拷贝方式,而memmove函数正好实现了所有重叠拷贝的情况

2.memmove

字符串与内存操作函数详解与模拟实现_第34张图片
memove函数实现了memcpy的所有功能而且允许目标和源重叠
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

模拟实现memmove函数

 void *my_memmove(void *dest, const void *src, size_t num)
 {
	 assert(dest != NULL);
	 assert(src   != NULL);
	 void *ret = dest;
	 if (src > dest)
	 {
		 //从前向后拷贝
		 while (num--)
		 {
			 *(char*)dest = *(char*)src;
			 ++(char*)dest;
			 ++(char*)src;
		 }
	 }
	 else
	 {
		 //从后向前拷贝
		 while (num--)
		 {
			 *((char*)dest + num) = *((char*)src + num);
		 }
	 }
	 return ret;
 }

字符串与内存操作函数详解与模拟实现_第35张图片

3.memcmp

字符串与内存操作函数详解与模拟实现_第36张图片
memcmp模拟实现

int my_memcmp(const void *str1, const void *str2, size_t num)
 {
	 assert(str1 != NULL);
	 assert(str2 != NULL);
	 while (--num)
	 {
		 if (*(char*)str1 != *(char*)str2)
		 {
			 break;
		 }
		 ++(char*)str1;
		 ++(char*)str2;
	 }
	return *(char*)str1 - *(char*)str2; 
 }

与strncmp基本一致

你可能感兴趣的:(玩转C语言,java,前端,javascript)