C进阶库函数:字符串函数和内存函数的模拟实现

今天我学习了C语言常见的几种库函数,我们来模拟实现一下!

字符串函数:

strlen()
strcpy()
strcat()
strcmp()
strncpy()
strncat()
strstr()

内存函数:

memcpy()
memmove()
memcmp()
memset()

字符串函数

函数细节代码内有详细的注释,望垂阅!

这是下面函数的接口代码,可以参考运行:

int main()
{
	
	//char str1[] = "hello world!";
	//char str2[] = "lo ";
	strstr返回char*,%c返回char,如果要得到一个字符,就需要对char解引用
	//printf("%c\n", *mystrstr(str1, str2));                       mystrstr
	//printf("%s\n", mystrstr(str1, str2));  
	///
	//char str1[] = "hello bit!";
	//char str2[] = "hello world!";                                mystrncmp
	//printf("%d\n",mystrncmp(str1,str2,7));
	///
	//char str1[1024] = "hello bit!";
	//char str2[] = "hello World!";                                mystrncat
	//printf("%s\n", mystrncat(str1, str2, 15));
	///
	//char str1[1024] = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x','x','x'};
	//char str2[] = "hello world!";                                mystrncpy
	//printf("%s\n",mystrncpy(str1,str2,7));
	///
	//char str1[] = "abcd";
	//char str2[] = "aecd";                                        mystrcmp
	//printf("%d\n", mystrcmp(str1, str2));
	///
	//char dest[1024] = "abcdefg";
	//char src[] = "hijklmn";                                      mystrcat
	//printf("%s\n", mystrcat(dest, src));
	///
	//char dest[] = "xxxxxxxxxxx";
	//char src[] = "hijklmn";                                      mystrcpy
	//printf("%s\n", mystrcpy(dest, src);t);
	/
}

strlen()

//strlen()
//      size_t strlen ( const char * str );
//计算‘\0’之前的字符个数
//注意条件:     ①返回值是 size_t        ②参数指向的字符串必须以‘\0’结束      ③参数是const char* 类型
//返回值: size_t          %d / %u
size_t mystrlen(const char* str)
{
	//检验不为空
	if (str == NULL)
	{
		return 0;
	}
	size_t size = 0;
	while (str[size] != '\0')
	{
		size++;
	}
	return size;
}

strcpy()

//strcpy()
//     char* strcpy(char * destination, const char * source );
//把 src 所指向的字符串复制到 dest。 如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
//返回一个指向最终的目标字符串 dest 的指针        char*

char* mystrcpy(char * dest, const char*src)
{
	//判断合法性
	assert(dest != NULL);
	assert(src != NULL);
	//赋值
	int i = 0;
	while (src[i] != '\0')
	{
		dest[i] = src[i];
		i++;
	}
	dest[i] = '\0 ';
	return dest;

strcat()

//strcat()
//         char * strcat ( char * destination, const char * source );
//把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。与strcpy一样,也需要dest的空间足够大,能够容纳两个字符串加在一起的长度
//注意:两个dest和src空间不能重叠  例如:
//                                      dest:  h  e  l  l  o  \0               src:  n  i  h  a  o  \0
//                                            0x100                                0x102
//返回一个指向最终的目标字符串 dest 的指针       char*

char* mystrcat(char* dest, const char* src)
{
	//判断合法性
	assert(dest != NULL);
	assert(src != NULL);
	//找到dest的结束位置'\0'
	int destTail = 0;
	while (dest[destTail] != '\0')
	{
		destTail++;
	}
	//拷贝直接使用mystrcpy(dest+destTail,src)即可,我们来写一下
	int i = 0;
	while (src[i] != '\0')
	{
		dest[destTail + i] = src[i];
		i++;
	}
	//把dest最后的位置设为'\0'
	dest[destTail + i] = '\0';
	//返回
	return dest;
}

strcmp()

//strcmp()
//          int strcmp(const char *str1, const char *str2)
//比较规则:按照字典序进行比较,谁在字典前面谁就小
//返回:  str1 < str2   ==>   < 0 的整数
//        str1 > str2   ==>   > 0 的整数              返回  int
//        str1 = str2   ==>   = 0 的整数

int mystrcmp(const char* str1, const char* str2)
{
	//先检验合法性
	assert(str1 != NULL);
	assert(str2 != NULL);
	//进行比较
	//没遇到'\0'的情况
	const char* p1 = str1;
	const char* p2 = str2;
	while (*p1 != '\0'&&*p2 != '\0')
	{
		if (*p1 < *p2){
			return -1;
		}else if (*p1>*p2){
			return 1;
		}else{
		//相等就比较下一个字符
			p1++;
			p2++;
		}
	}
	//遇到'\0'的情况
	//如果两个字符串数量不一样,比如“abcd”和“ab”,就会出现有一方先到'\0',此时它的值就是0
	if (*p1 < *p2){
		//p1先结束了(p1到'\0'时,*p1=0)
		return -1;
	}
	else if (*p1>*p2){
		//p2先结束了
		return 1;
	}
	else{
		//两个同时遇到'\0'了
		return 0;
	}
	//上面这一串代码可以用一句代码概况:  return *p1-*p2;       实际的strcmp()也是这么做的
}

strncpy()

//strncpy()
//      char *strncpy(char *dest, const char *src, size_t n)
//拷贝num个字符从源字符串到目标空间,
//						当num比src长时,会在多余部分补上'\0'
//						当num比src短时,就只拷贝src的前num个字符,不会拷贝'\0'
//						num的值,必须保证dest能够容纳下,且要考虑'\0'的情况
//	该函数返回最终复制的字符串dest                char*

char* mystrncpy(char* dest, char* src, size_t num)
{
	//检验合法性 
	assert(dest != NULL);
	assert(dest != NULL);
	assert(dest != 0);
	//开始拷贝
	size_t i = 0;            //后面会进行i和num的比较,需要是同一类型
	while (src[i] != '\0'&&i < num)
	{
		dest[i] = src[i];
		i++;
	}
	//有两种情况会打断上面循环
	// a. i == num  ,直接return即可
	if (i == num)
		return dest;
	// b. src遇到'\0',把后面的dest剩余部分设为'\0'
	while (i < num)
	{
		dest[i] = '\0';
		i++;
	}
	return dest;
}

strncat()

//strncat()
// char *strncat(char *dest, const char *src, size_t n) 
//把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
//返回dest  char*

char* mystrncat(char* dest, const char* src, size_t num)
{
	//检验合法性
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);
	//找到dest的末尾
	size_t destTail = 0;
	while (dest[destTail] != '\0')
	{
		destTail++;
	}
	//循环结束destTail指向dest的'\0'
	//把src从destTail的位置开始拷贝给dest
	size_t i = 0;
	while (src[i] != '\0' && i < num)
	{
		dest[destTail] = src[i];
		i++;
		destTail++;
	}
	//两种可能导致循环结束:
	//   a.  src遇到'\0',此时需要将'\0'拷贝上去
	//   b.  i = num,说明src比num长,我们只需要拷贝前面的就可以了,后面的用'\0'填充就好了
	//  a b 两种情况都要添加'\0',那么我们直接加上就可以了
	dest[destTail + 1] = '\0';
	return dest;
}

strncmp()

//strncmp()
//       int strncmp(const char *str1, const char *str2, size_t n) 
//把 str1 和 str2 进行比较,最多比较前 n 个字节    比较和返回规则跟strcmp一样

int mystrncmp(const char* str1, const char*str2, size_t num)
{
	//检验合法性
	assert(str1 != NULL);
	assert(str2 != NULL);
	assert(num != 0);
	//一个一个进行比较
	size_t i = 0;
	while (str1 != '\0'&&str2 != '\0'&&i < num)
	{
		if (str1[i] < str2[i])
			return -1;
		else if (str1[i]>str2[i])
			return 1;
		else
			i++;
	}
	//有三种情况会导致上面的循环结束
	//str1 遇到'\0',
	//str2 遇到'\0',        这两种认为是一种,谁先遇到谁小
	//i == num, 此时说明前面的str1[i],str2[i]都相等,且比较了前num个字符,完成比较,认为str1 = str2
	if (i == num)
		return 0;
	return str1[i] - str2[i];
}

strstr()

//strstr()
//         char *strstr(const char *haystack, const char *needle) 
//在字符串 haystack 中查找第一次出现字符串 needle 的位置,
//找到返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。       char*

const char* mystrstr(const char* str1, const char* str2)
{
	//检验合法性
	assert(str1 != NULL);
	assert(str2 != NULL);
	//两个字符串也不能为空字符串str != "";
	assert(str1 != '\0');
	assert(str2 != '\0');
	//算法
	const char* black = str1;
	//外层循环控制black从str1找子字符串的起始位置
	while (*black!='\0')
	{
		const char* red = black;
		const char* sub = str2;
		//里层循环从black开始,判断当前字符串与str2是否相等
		while (*red != '\0'&&*sub != '\0'&&*red == *sub)
		{
			red++;
			sub++;
		}
		//上面这个循环有三种可能会终止
		// *red = '\0' ,就是从black当前的位置找没找到,可以直接进入下次循环.或者说找不到了,直接返回NULL
		// *sub = '\0' ,没找到,跳出进入下次循环。
		// *red == *sub ,找到了,直接返回black。
		if (*red != '\0'&&*sub != '\0')
		{
			black++;
			continue;
		}
		return black;
	}
	return NULL;
}

内存函数

这是下面函数的接口代码,可以参考运行:


typedef struct Student
{
	int id;
	char name[1024];
}student;

int main()
{
	//int a = 10;
	//int b = 0;
	//mymemcpy(&b,&a, sizeof(a));
	//printf("b = %d\n", b);


	//int arr[] = { 1, 2, 3, 4, 5 };
	//int arr1[100] = { 0 };
	//mymemcpy(arr1, arr, sizeof(arr));
	//for (int i = 0; i < 5; i++)
	//{
	//	printf("%d\n",arr1[i]);
	//}
	

	//student s1 = { 1, "lihan" };
	//student s2;
	//mymemcpy(&s2, &s1, sizeof(student));
	//printf("%d,%s",s2.id,s2.name);


	//char str1[] = "hello";
	//char str2[1024];
	//mymemcpy(str2, str1, sizeof(str1));
	//printf("%s\n",str2);
}


memcpy()

//memcpy()
//void *memcpy(void *str1, const void *str2, size_t n) 
// 从存储区 str2 复制 n 个字节到存储区 str1。     (内存块拷贝)
//返回值:    返回一个指向目标存储区 str1 的指针           void*
//注意: 这里的字符串是 void* 类型,意味着它再也不能解引用操作。因为void*只知道地址,不知道大小。
//       我们就只有对他进行强制类型转换,为了便于操作,我们转为char*。  其他类型并不方便

void* mymemcpy(void* str1, const void* str2, size_t num)
{
	//检验合法性
	assert(str1 != NULL);
	assert(str2 != NULL);
	assert(num != 0);
	//void*不好操作,进行强制类型转换   按照字节为单位进行转换
	char* pstr1 = (char*)str1;
	const char* pstr2 = (const char*)str2;
	size_t i = 0;
	while (i < num)
	{
		pstr1[i] = pstr2[i];
		i++;
	}
	return str1;
}

memmove()

//memmove()
//void *memmove(void *str1, const void *str2, size_t n)
//比memcpy增加了考虑内存重叠,如果str1和str2内存重叠,那么memmove比memcpy更安全,如果不重叠,两者相同
//如果重叠方式是这样的:
//                          int a[100]:     1        2        3        4
//                                         src               dest
//                                        从1开始          从3开始 
//把src考入dest,如果按照正常手法,就会将3覆盖,怎么办呢?
//我们可以反着拷贝,把4拷贝到最后面,再拷贝3 ……
//                                                           ④ 1     ③ 2      ② 3      ① 1
//但如果重叠方式是这样的:
//                          int a[100]:               1        2       3       4
//                                          dest              src
//                                         从src前面开始    从2开始
//按照正常的手法一个字节一个字节拷贝,并没有什么影响
//所以我们考虑内存重叠只需要考虑dest起始位置在src和src+num就好了

void* mymemmove(void* dest, void* src, size_t num)
{
	//检验合法性
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);
	//void*不好操作,进行强制类型转换   按照字节为单位进行转换
	char* pdest = (char*)dest;
	const char* psrc = (const char*)src;
	size_t i = 0;
	//判断重叠
	if (pdest>psrc && pdest<psrc+num)
	{
		//如果重叠,反着拷贝
		size_t i = num;
		while (i>0)
		{
			pdest[i] = psrc[i];
			i--;
		}
		return dest;
	}
	else
	{
		//如果不重叠,正着拷贝
		size_t i = 0;
		while (i < num)
		{
			pdest[i] = psrc[i];
			i++;
		}
		return dest;
	}
}

memcmp()

// memcmp()
//         int memcmp(const void *str1, const void *str2, size_t n)) 
//把存储区 str1 和存储区 str2 的前 n 个字节进行比较      比较内存块
// 返回值和strcmp类似,返回0、大于0/小于0的数字      int


int mymemcmp(const void* str1, const void* str2, size_t num)
{
	//检验合法性
	assert(str1 != NULL);
	assert(str2 != NULL);
	assert(num != 0);
	//void*无法比较,强制类型转换
	const char* pstr1 = (char*)str1;
	const char* pstr2 = (char*)str2;
	//一个字节一个字节进行比较
	for (size_t i = 0; i < num; i++)
	{
		if (pstr1[i] < pstr2[i])
		{
			return -1;
		}
		else if (pstr1[i]>pstr2[i])
		{
			return 1;
		}
		else
			continue;
	}
	return 0;
}

memset()

//memset()
//void *memset(void *str, int c, size_t n)
//复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。   填充
//     返回一个指向存储区 str 的指针。      void*

void* mymemset(void* str, int value, size_t num)
{
	//检验合法性
	assert(str != NULL);
	assert(num != 0);
	//类型转换
	char* sptr = (char*)str;
	//一个字节一个字节填充
	for (size_t i = 0; i < num; i++)
	{
		sptr[i] = (char)value;
	}
	//循环结束,返回值
	return str;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值