字符串函数和内存函数的模拟实现

本文详细介绍了C语言中strncpy,strncat,strlen,strstr四个字符串操作函数以及memcpy和memmove两个内存操作函数的原理和模拟实现,帮助读者理解这些函数的工作机制和使用场景。
摘要由CSDN通过智能技术生成

目录

前言 

 1. 字符串操作

1.1 strncpy 

1.1.1 讲解

1.1.1 模拟实现

1.2 strncat

1.2.1 讲解

1.2.2 模拟实现

2. 字符串检验

2.1 strlen

2.1.1 讲解

2.1.2 模拟实现

2.2 strstr

2.2.1 讲解

2.2.2 模拟实现

3. 字符数组操作

3.1 memcpy

3.1.1 讲解

3.1.2 模拟实现 

3.2. memmove

3.2.1 讲解

3.2.2 模拟实现


前言 

c语言定义在<string.h>的函数有很多种类(空终止字节字符串 - cppreference.com)我们今天模拟实现以下几种:

1.字符串操作:strncpy,strncat

2.字符串检验:strlen,strstr

3.字符数组操作:memcpy,memmove

 1. 字符串操作

1.1 strncpy 

1.1.1 讲解

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest。 n表示最多复制的字符。最后会返回desk的地址。举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "never give up";
	char arr2[50] = "";
	printf("%s\n", strncpy(arr2, arr1,7));
	return 0;
}

输出结果:never g

我们想将arr1中的数据全部“传输”到arr2中,一般会想到利用for 循环将arr1中的一个元素一个一个拷进去,但其实也可以用strncpy一步到位,7(n),表示复制的字符数。这时,有些小伙伴可能就要问了:如果你要求的字符数大于arr1会怎样?答:不会怎样,\0传进去了,后面传再多也没用了。

1.1.1 模拟实现

首先,凭借着参数及返回值的讲解,我们的函数声明首先就上来了。

char* my_strncpy(char* desk, const char* src, size_t num) ;

有特殊到一般,传了空指针没法处理,返回NULL,正常传进来,处理,也当然会很自然的想到循环。循环num次,一个一个拷贝。

// 模拟实现strncpy

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

char* my_strncpy(char* desk, const char* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	char* ret = desk;
	while (num--)
	{
		*desk++ = *src++;
	}
	return ret;
}

int main()
{
	char arr1[] = "never give up";
	char arr2[50] = "";
	printf("%s\n", my_strncpy(arr2, arr1,strlen(arr1)+1));
	return 0;
}

该段代码简练的一点是使用了后置++,减少了代码量。注意要返回原来的地址,所以还要再创建一个指针变量储存,最后好返回。

1.2 strncat

1.2.1 讲解

C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。举个例子:

#define _CRT_SECURE_NO_WARINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[49] = "never ";
	char arr2[] = "give up";
	printf("%s\n", strncat(arr1, arr2, 6));
	return 0;
}

输出结果:never give u

就好像是把arr1的从\0开始替换成了arr2,前六个字符一样。

1.2.2 模拟实现

你可以这么想象,arr1\0前面是一个数组,从\0开始后面又是一个数组,strncat相当于把arr2中的内容strcpy到了那个后面的数组一样。

 模拟实现strncat
//
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

char* my_strncat(char* desk, const char* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	char* ret = desk;
	while (*desk++)
	{
		;
	}
	desk--;
	while (num--)
	{
		*desk++ = *src++;
	}
	return ret;
}

int main()
{
	char arr1[49] = "never ";
	char arr2[] = "give up";
	printf("%s\n", my_strncat(arr1, arr2, 6));
	return 0;
}

所以,我们先就是找到了\0的地址,其余跟strncpy简直一模一样!

2. 字符串检验

2.1 strlen

2.1.1 讲解

相信大家对strlen函数都不陌生了吧,就是返回\0之前的字符数。

2.1.2 模拟实现

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* arr)
{
	assert(arr);
	const char* start = arr;
	while (*arr++)
	{
		;
	}
	
	return arr - start;
}

int main()
{
	char arr[] = "never give up";
	printf("%zd\n", my_strlen(arr));
	return 0;
}

常见的计数器实现就不过多介绍了,以上便是指针 - 指针的形式。

2.2 strstr

2.2.1 讲解

在wps办公中,我们常有一个查找和替换,strstr就是一个类似于查找功能的函数。char * strstr ( const char * str1, const char * str2); 找的就是str2在str1中首次出现的位置。注意不单检测str1中有没有str2,没有返回空指针,有返回首次出现的地址

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "abbcdefghij";
	char arr2[] = "bcdef";
	printf("%s\n", strstr(arr1, arr2));
	return 0;
}

 输出:bcdefghij

2.2.2 模拟实现

我相信大家开始的思路应该都是在arr1中开始遍历,先找到与arr2第一个字符一样的字符再说,然后看看第二个是否相等,不相等找到的不是,下一个;相等,看看第三个……

由于我们相当于是检验找到前那个元素是否满足要求,肯定就涉及到了while循环。确认第一个字符对不对跟确认第二个,第三个对不对是不是又循环了?你地址在那里变换,如果不是,是不是该储存一个变量来储存,好回滚?

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

// 函数返回substr在str中第一次出现时的地址
char* my_strstr(const char* str, const char* substr)
{
	assert(str);
	assert(substr);
	
	char* temp1 = NULL;
	char* temp2 = NULL;
	int flag = 0;
	while (*substr && *str)
	{
		if (*str++ != *substr)
		{
			if (flag)
			{
				str = ++temp1;
				substr = temp2;
				flag = 0;
				continue;
			}
			temp1 = str;
			temp2 = substr;
		}
		else
		{
			substr++;
			flag = 1;
		}
	}
	if (!flag || *substr)
		return NULL;

	return temp1;
}

int main()
{
	char arr1[] = "abbiijkt";
	char arr2[] = "ijkt";
	char* ret = my_strstr(arr1, arr2);
	printf("%s\n", arr1);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("no find\n");
	return 0;
}

或者说,也可以这么理解,有2个指针跟str有关,有2个指针跟str2有关,其中一种指针的作用是找到并保存str中某个字符跟substr中某个字符相等的地址,另一种指针就看第二个,第三个……是否跟substr2一样,直到\0都一样就返回str前一种指针了,不一样,str前一种指针加1就行了。两个后一个指针回到该回的地方。

3. 字符数组操作

3.1 memcpy

3.1.1 讲解

它跟strncpy很像,都是针对内存进行操作的,但strncpy干不了的,memcpy可以干,因为memcpy可以接收任意类型的指针。C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
	char arr1[49] = "";
	char arr2[] = "never give up";
	printf("%s\n", (char*)memcpy(arr1, arr2 + 1,sizeof(char) * 12));
	int arr3[10] = { 0 };
	int arr4[] = { 1,2,3,4,5,6,7,8,9 };
	memcpy(arr3, arr4, sizeof(int) * 6);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

输出结果:

ever give up
1 2 3 4 5 6 0 0 0 0

这里要注意的是由于memcpy接收的是任意类型的数据,所以也无法判定是什么数据类型,有多少个元素,传过去的是字节数,不是字符数

3.1.2 模拟实现 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* desk, const void* src, size_t num)
{
	if (!desk || !src)
		return NULL;
	void* ret = desk;
	while (num--)
	{
		*((char*)desk)++ = *((char*)src)++;
	}
	return ret;
}

int main()
{
	char arr1[49] = "";
	char arr2[] = "never give up";
	printf("%s\n", (char*)my_memcpy(arr1, arr2 + 1,sizeof(char) * 12));
	int arr3[10] = { 0 };
	int arr4[] = { 1,2,3,4,5,6,7,8,9 };
	my_memcpy(arr3, arr4, sizeof(int) * 6);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

其实,不必因为她接收的是无类型的指针就觉得怎样怎样,就多了一个强制类型转换,如此而已。

3.2. memmove

3.2.1 讲解

其实这个函数跟memmove又是很像,memcpy能干的,memmove能干,memcpy可能干不了的(取决于编译器),memmove能干,memmove多出来的一个功能便是当arr1与arr2有重叠部分时,仍然可以很好的处理。由于在vs2022下memcpy已经设计得跟memmove差不多了,就只演示memmove的了


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>


int main()
{
	char arr[] = "abbcdefg";
	printf("%s\n", arr);
	printf("%s\n", (char*)memmove(arr + 2, arr, 5));
	return 0;
}

3.2.2 模拟实现

这里主要是不同情况拷贝顺序的问题,这么做是为了防止 src中要拷贝给desk中的数因为重叠而被篡改,其他就都跟memcpy一样了。除此以外,从后往前拷,多面的地址通过指针运算就可以得到了,不要陷进去了。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memmove(void* desk, void* src, size_t num)
{
	if (!desk || !src)
	{
		return NULL;
	}
	void* ret = desk;
	if (desk <= src)
	{
		while (num--)
		{
			*((char*)desk)++ = *((char*)src)++;
		}
	}
	else
	{
		while (num--)
		{
			*(((char*)desk) + num) = *(((char*)src) + num);
		}
	}
	return ret;
}

int main()
{
	char arr[] = "abbcdefg";
	printf("%s\n", arr);
	printf("%s\n", (char*)my_memmove(arr + 2, arr, 5));
	return 0;
}

好了,今天字符串函数和内存函数的模拟实现就分享到这里了。

感谢各位大佬的支持和指正

  • 40
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 40
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值