C-模拟实现 字符串函数str系列、内存函数mem系列

 

 
编译环境:vs2017
编译的时候注意包含头文件。
 
  在学习字符串函数之前,先看看size_t和int的区别吧:

  • size_t是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int。size_t在32位架构上是4字节,在64位架构上是8字节。
  • int是一个数据类型,应为signed int,在32位和64位上都是4个字节。
     
     

  在我的上一篇博客里边已经讲了 strlen() 和 sizeof 的区别,那么在这里稍加概括:

  • strlen()是函数,参数必须是字符型指针(char*), 且以’\0’结尾,返回的长度大小不包括’\0’。
  • sizeof是运算符,返回对象的字节大小,包括’\0’。
  • 辨析:
	char str[] = "hftr\0fv\\0fgy\\\0mfg";
	printf("%d\n", strlen(str)); // 4
	printf("%d\n", sizeof(str)); // 18

在这里插入图片描述
再介绍一个会用到的断言函数,assert() 函数:

  • 头文件是 #include <assert.h>
  • 如果它的条件返回错误,则终止程序执行
  • 原型是:void assert( int expression )

 
 

字符串函数

 

1、先来看一个最简单的字符串函数:strlen() (长度 --> 重要)

  • strlen() 所做的仅仅是一个计数器的工作
  • 碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)
  • 原型:  size_t  strlen ( const char *  string ),返回值千万不能搞错!!!!!!
     
    模拟实现strlen():
size_t my_strlen(const char *string)
{
	assert(string);
	size_t count = 0;
	while (*string)
	{
		++count;
		++string;
	}
	return count;
}

int main()
{
	char string[] = "abcdef\0abcd";
	printf("%u\n", my_strlen(string));  // 6
	system("pause");
	return 0;
}

 

2、常用字符串函数之一:strcpy() (拷贝 --> 重要)

  • strcpy() 把一个字符串拷贝(覆盖)到另一个字符串, '\0’也会拷进去
  • 目标字符串要能装下源字符串
    注意:
  • 如果源字符串和目标字符串重叠,strcpy 行为不确定。
  • 由于 strcpy 不检查在 strDestination中是否有足够的空间,所以可能会产生缓冲区溢出的问题。
    解决:strcpy之前判断一下。长了就抛弃!
  • 原型:  char*  strcpy  (char*  destination,  const  char*  source),注意返回值!!!!!!
     
     
    模拟实现strcpy():
// 模拟实现strcpy

char* my_strcpy(char* des, const char* src)
{
	assert(des&&src);
	char* ret = des;
	while (*des = *src)
	{
		des++;
		src++;
	}
	return ret;
}
int main()
{
	char des[100];
	char src[100];
	printf("请输入源字符串:");
	scanf("%s", src);
	printf("请输入目标字符串:");
	scanf("%s", des);
	printf("更改后的目标字符串:%s\n", my_strcpy(des, src));
	system("pause");
	return 0;
}

 

3、常用字符串函数之一:strstr() (查找子串 --> 重要)

  • strstr() 返回要查找的字符串在目标字符串中的位置
  • 比如:在"123abcd12345de6789123efg"中查找"12345de",返回的是"12345de6789123efg"
  • 原型: char* my_strstr(const char* string, const char* strCharSet),注意返回值!!!!!!
     
     
    模拟实现strstr():
//模拟实现strstr(判断子串)

char* my_strstr(const char* string, const char* strCharSet)
{
	assert(string&&strCharSet);
	while (*strCharSet)
	{
		const char* p = string;
		const char* q = strCharSet;
		while (*p == *q && *q)
		{
			++p;
			++q;
		}
		if (*q == '\0')
			return string;
		
		++string;
		if (*string == '\0')
		{
			return NULL;
		}
	}
}
int main()
{
	char string[100];
	char strCharSet[100];
	printf("请输入目标字符串:");
	scanf("%s", string);
	printf("请输入要查找的字符串:");
	scanf("%s", strCharSet);
	if (my_strstr(string, strCharSet) == NULL)
	{
		printf("没有找到!\n");
	}
	else printf("要查找的字符串在目标字符串的位置是:%s\n", my_strstr(string, strCharSet));
	system("pause");
	return 0;
}

 

4、常用字符串函数之一:strcmp() (比较)

 

  • strcmp() 比较两个字符串str1和str2,
     当str1>str2时,函数返回一个大于0的数
     当str1=str2时,函数返回 0
     当str1<str2时,函数返回一个小于0的数

  • 原型: int strcmp(const char* des, const char* src),注意返回值!!!!!!
     
     

  • 下面的一定要注意了!!!!!!


如果一个字符值的ASCII码值超过127,该如何比较?也就是说,这类字符是非常规、不常用的字符。如字符¥的ASCII码值在850 (Latin 1)为190,将其和'a'比较,得到的结果可能会和你想的不一样。超过127的字符在处理时应该统一将其转换为对应的无符号型。
这就是代码中会出现 ** (unsigned char * )** 的原因。     模拟实现strcmp():
//模拟实现strcmp(比较)

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1&&str2);
	while (*str1&&*str2)
	{
		if (*(unsigned char*)str1 > *(unsigned char*)str2)
		{
			return 1;
		}
		else if (*(unsigned char*)str1 < *(unsigned char*)str2)
		{
			return -1;
		}
		else
		{
			++str1;
			++str2;
		}
	}
	if (*str1 != '\0')
	{
		return 1;
	}
	else if (*str2 != '\0')
	{
		return -1;
	}
	else return 0;
}
int main()
{
	char str1[100];
	char str2[100];
	printf("请输入第一个字符串:");
	scanf("%s", str1);
	printf("请输入第二个字符串:");
	scanf("%s", str2);
	printf("比较结果:%d\n", my_strcmp(str1, str2));
	system("pause");
	return 0;
}

 

5、常用字符串函数之一:strcat()  (拼接)

 

  • strcat() 把一个字符串拼接到另一个字符串上,拼接到目标字符串后,覆盖掉’\0’
  • 目标字符串必须足够大
  • 原型: char* strcat (char* destination, const char*  source),注意返回值!!!!!!
     
     
    模拟实现strcat():
// 模拟实现strcat(拼接)

char* my_strcat(char* des, const char* src)
{
	assert(des&&src);
	char* ret = des;
	
	while (*des)
		++des;
		
	while (*src)
	{
		*des++ = *src++;
	}
	*des = '\0';
	return ret;
}
int main()
{
	char des[100];
	char src[10];
	printf("请输入源字符串:");
	scanf("%s", src);
	printf("请输入目标字符串:");
	scanf("%s", des);
	printf("目标字符串修改为:%s\n", my_strcat(des, src));
	system("pause");
	return 0;
}

 

6、常用字符串函数之一:strchr() (字符串中查找字符)

 

  • strchr() 查找字符串s中首次出现字符c的位置。
  • 原型: char* my_strchr(const char* string, int c),注意返回值!!!!!!
     
     
    模拟实现strchr():
// 模拟实现strchr(串中找字符)

char* my_strchr(const char* string, int c)
{
	assert(string);
	while (*string)
	{
		if (*string == c)
		{
			return string;
		}
		++string;
	}
	return NULL;
}
int main()
{
	char string[100];
	int c;
	printf("请输入一个字符串:");
	scanf("%s", string);
	c = 'b';
	if (my_strchr(string, c) == NULL)
	{
		printf("没有找到!\n");
	}
	else printf("%c的位置是:%s\n", c,my_strchr(string, c));
	system("pause");
	return 0;
}

 

7、常用字符串函数之一:strncpy() (拷贝字符串片段)

 

  • strncpy() 把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中, 如果src不够n个,则追加’\0’,直至num个, 并返回被复制后的dest,复制后的dest打印时遇到第一个’\0’就停止。
  • 只是将src的前n个字符复制到dest的前n个字符,后面不自动添加’\0’,也就是结果dest中的src后面不包括’\0’,需要再手动添加一个’\0’。
  • 原型: char* my_strncpy(char* dest, const char* src, size_t num),注意返回值!!!!!!
     
     
    模拟实现strncpy():
// 模拟实现strncpy

char* my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest&&src);
	char* ret = dest;
	while (num)
	{
		--num;
		*dest++ = *src++;
	}
	if (num > 0)  // src的个数不够需要拷贝的字符个数,后面追加'\0'
	{
		while (num--)
		{
			*dest++ = '\0';
		}
	}
	return ret;
}
int main()
{
	char des[50] = { NULL };
	char src[50] = { NULL };
	size_t num = 0;
	printf("请输入目标字符串:");
	scanf("%s", des);
	printf("请输入源字符串:");
	scanf("%s", src);
	printf("请输入拷贝个数:");
	scanf("%u", &num);
	my_strncpy(des, src, num * sizeof(char));
	printf("%s\n", des);
	system("pause");
	return 0;
}

 

8、常用字符串函数之一:strncat() (拼接字符串片段)

 

  • strncat() 把src中n个元素拼接到dest的’\0’之前,src拼接到dest完成后,在dest后追加一个’\0’,dest后面还有它自带的’\0’。
  • 原型: char* my_strncat(char* des, const char* src, size_t num),注意返回值!!!!!!
     
     
    模拟实现strncat():
char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest&&src);
	char* ret = dest;
	while (*dest)
	{
		++dest;
	}
	while (num-- && *src)
	{
		*dest++ = *src++;
	}
	*dest = '\0';
	return ret;
}
int main()
{
	char des[50] = { NULL };
	char src[50] = { NULL };
	size_t num = 0;
	printf("请输入目标字符串:");
	scanf("%s", des);
	printf("请输入源字符串:");
	scanf("%s", src);
	printf("请输入拼接个数:");
	scanf("%u", &num);
	printf("拼接后,目标字符串为:%s\n", my_strncat(des, src, num));
	system("pause");
	return 0;
}

 

9、常用字符串函数之一:strncmp() (比较字符串片段)

 

  • strncmp() 比较两个长度为n的字符串str1和str2,
     当str1>str2时,函数返回一个大于0的数
     当str1=str2时,函数返回 0
     当str1<str2时,函数返回一个小于0的数
  • 原型: int my_strncmp(const char* str1, const char* str2, size_t num),注意返回值!!!!!!
  • 还有一点,请借鉴上述strcmp()函数。
     
     
    模拟实现strncmp():
// 模拟实现strncmp()

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1&&str2);
	while (num && (*str1) && (*str2))
	{
		if (*(unsigned char*)str1 > *(unsigned char*)str2)
		{
			return 1;
		}
		else if (*(unsigned char*)str1 < *(unsigned char*)str2)
		{
			return -1;
		}
		else
		{
			++str1;
			++str2;
			--num;
		}
	}
	if (num == 0)
	{
		return 0;
	}
	else
	{
		if ((*str1 == '\0') && (*str2 != '\0'))
		{
			return -1;
		}
		else if ((*str1 != '\0') && (*str2 == '\0'))
		{
			return 1;
		}
		else return 0;
	}
}
int main()
{
	char str1[50] = { NULL };
	char str2[50] = { NULL };
	size_t num = 0;
	printf("请输入第一个字符串:");
	scanf("%s", str1);
	printf("请输入第二个字符串:");
	scanf("%s", str2);
	printf("请输入比较个数:");
	scanf("%u", &num);
	printf("比较结果是:%d\n", my_strncmp(str1, str2, num));
	system("pause");
	return 0;
}


库函数

 

memcpy() VS memmove()

两者都是从原数组中拷贝一定长度的元素到目标数组中。

内存重叠问题:
在这里插入图片描述

1、memcpy() (任意类型拷贝 --> 重要)

  • 发生内存重叠时,不保证正确性。
  • 从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。
  • 原型: void* memcpy(void* destin, void* source, unsigned n)
  • 头文件:<string.h>
     
     
    模拟实现memcpy():

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

void* my_memcpy(void* dest, const void* src, size_t sz)
{
	assert(dest&&src);
	char* dest_ = (char*)dest;
	char* src_ = (const char*)src;
	for (size_t i = 0; i < sz; ++i)
	{
		dest_[i] = src_[i];
	}
	return dest;
}

int main() 
{
	int dest[10] = { 0,1,2,3,4,5 };
	int src[] = { 8,8,8 };
	my_memcpy(dest, src, sizeof(src));
	for (int i = 0; i < 10; ++i)
	{
		printf("%d ", dest[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}



2、memmove() (拷贝–> 重要)

  • 发生内存重叠时,保证正确性。
  • 由src所指内存区域复制count个字节到dest所指内存区域。
  • 原型: void* memmove(void* dest, const void* src, size_t count)
  • 头文件:<string.h>
     
     
    模拟实现memmove():
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

void* my_memmove(void* dest, const void* src, size_t size)
{
	assert(dest&&src);
	char* dest_ = (char*)dest;
	const char* src_ = (const char*)src;
	if ((dest_ > src_)&&(dest_ < src_ + size))
	{
		for (size_t i = size - 1; i >= 0; --i)//从后往前拷,存在内存重叠问题
		{
			dest_[i] = src_[i];
		}
	}
	else
	{
		for (size_t j = 0; j < size; ++j)//从前往后拷,不存在内存重叠问题
		{
			dest_[j] = src_[j];
		}
	}
	return dest;
}

int main()
{
	int dest[10] = { 0,1,2,3,4,5,6,7,8,9 };


	//int src[] = { 9,9 };                     // 1.把src中的所有拷贝到dest中(两个数组,不存在内存重叠问题)
	//my_memmove(dest, src, sizeof(src));        
	
	
	my_memmove(dest, dest+3, 2*sizeof(int));   // 2.把dest中的3和4拷贝到dest中(一个数组,存在内存重叠问题)
											   // dest+3  :  从下标为(0+3)处开始拷贝
											   // 2*sizeof(int)  :  拷贝2*4个字节,就是两个int元素 3和4
	
	
	for (size_t i = 0; i < 10; ++i)            
	{                                          
		printf("%d ", dest[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值