字符操作库函数以及内存操作库函数 C语言实现

字符操作库函数

  • strlen

strlen判断结束的标志为找到字符串中的'\0',也就是说如果字符串中间出现'\0'将会导致strlen停止 即strlen的返回值是'\0'前所出现的字符个数   然而此时字符串长度并非真实长度,但一般很少出现字符串中间会有‘\0’

size_t strlen(const char* str)

模拟实现my_strlen:

方式1:直接计数  遍历整个字符串 直至找到“\0”为止  否则计算器实现加一操作

int my_strlen(const char* str)
{
    int count=0;
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}

方式2:不引入整型计数变量,利用递归实现计数

int my_strlen(const char* str)
{
    if(*str=='\0')
    {
       return 0;
    }
    return 1+my_strlen(str+1);
}

方式3:指针方式实现计数,遍历不为‘\0’则向右移动指针,直至指针p移动至‘\0’前一位置,p-str即为中间不为'\0'的字符个数

int my_strlen(const char* str)
{
    char* p=(char*)str;
    while(*p!='\0')
    {
        p++;
    }
    return p-str;
}
  • strcpy

源字符串必须以'\0'结束,同时,目标空间必须足够大,以免源字符串拷贝空间不足,通过实际代码调试查看内存空间  注意到,拷贝过程中会将源字符串结尾的‘\0’拷贝至目标地址空间中

char* strcpy(char* destination,const char* source)

my_strcpy模拟实现:

当判断源字符串不为空,以及目标空间存在的情况下,实现从源字符串到目标空间的逐个字符拷贝,直至源字符串遇到'\0'结束拷贝

注意:while(*dest++=*src++);  当src遇到'\0',赋值给dest后,表达式结果为0,为false,则循环结束,暂停拷贝

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

char* strcat(char* destination,const char* source)

strcat模拟实现,可以在strcpy基础上进行,即在目标空间末尾(不包括‘\0’)进行一次strcpy即可实现,具体参见代码

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

考虑:strcat自己给自己追加会发生什么?

自己给自己追加的过程中,源字符串末尾的‘\0’被覆盖,导致while循环进入死循环,无法再找到‘\0’停止,此时starcat将一直拷贝连接 直至目标空间溢出报错

  • strcmp

strcmp实现字符串比较,通过对应字符ASCll码比较大小

int strcmp(const char* src1,const char* src2)

如果第一个字符串大于第二个字符串,返回大于0的数

如果第一个字符串等于第二个字符串,返回等于0的数

如果第一个字符串小于第二个字符串,返回小于0的数

strcmp的模拟实现:

int my_strcmp(const char* src1,const char* src2)
{
    int ret=0;
    assert(src1!=NULL);
    assert(src2!=NULL);
    while(!(ret=*(unsigned char*)src1-*(unsigned char*)src2)&&*src2)//相等字符则后移比较,直至遇到‘\0’
    {
        src1++;
        src2++;
    }
    if(ret<0)
        ret=-1;
    else if(ret>0)
        ret=1;
    return ret;
}
  • strstr

strstr实现子串查找,返回子串首元素地址

char* strstr(const char*,const char*)

模拟实现my_strstr():主要难点在于一个字符一个字符逐一匹配,需要一个指针记录起始位置,每次匹配失败需要退回起始位置的下一位置处开始在次匹配

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

char* my_strstr(const char* str1,const char* str2)
{
	assert(str1);
	assert(str2);

	const char* s1 = str1;
	const char* s2 = str2;
	const char* cp = str1;//该指针记录每一次进行匹配的起始位置

	if (*str2 == '\0')
	{
		return (char*)str1;
	}
	while (*cp)
	{
		//一次匹配查找的过程
		s1 = cp;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)//逐个匹配
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')//只有s2走完才能说明找到真正的子串  返回起始查找位置cp  s1走完还没有找到匹配的子串,则返回null
		{
			return (char*)cp;
		}
		cp++;
	}
	return NULL;
}

int main()
{
	char* str1 = "abcdef";
	char* str2 = "cde";
	char* ret = my_strstr(str1,str2);
	printf("%s",ret);
	return 0;
}

kmp算法实现字符串匹配详细可参考http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

内存操作库函数

  • memcpy

函数从source拷贝nums个字节到destination中,此时拷贝字节,遇到'\0'并不会停止

void* memcpy(void* destination,const void* source,size_t nums)

memcpy拷贝过程中 源内存块和目标内存块不可以重叠

由于类型不确定,为void*类型,为了实现一字节一字节拷贝,需要强制类型转换为char*类型具体代码如下:

void* my_memcpy(void* dest,const void* str,size_t count)
{
	void* ret = dest;
	//一字节一字节拷贝,一字节一字节后移
	while (count--)
	{
		*(char*)dest = *(char*)str;
		dest = (char*)dest + 1;
		str = (char*)str + 1;
	}
	return ret;
}
  • memmove

memmove与memcpy功能基本类似,都是实现内存块数据拷贝,memmove可以实现重叠内存拷贝

重叠内存拷贝      不同的拷贝方向会导致尚未拷贝的内存空间 cd被覆盖为bb  导致后续拷贝也只拷贝了bb    如图所示:

解决方案,从source后面开始拷贝,从后向前拷贝,则不会被覆盖,但情况不一定,如果红色框为dest,绿色框为source,从后向前拷贝又会被覆盖

考虑是否存在一个分界条件,使得分情况处理即可

如果拷贝的目的地址在源地址前面  则源地址从前向后拷贝到目的地址

如果拷贝的目的地址在源地址后面  则源地址从后向前拷贝到目的地址

void* my_memmove(void* dest, const void* str, size_t count)
{
	//避免拷贝重叠  需要分情况讨论
	void* ret = dest;
	if (dest < str)//如果拷贝的目的地址在源地址前面  则源地址从前向后拷贝到目的地址
	{
		while (count--)
		{
			//依然遵循一字节一字节拷贝
			*(char*)dest = *(char*)str;
			dest = (char*)dest + 1;
			str = (char*)str + 1;
		}
	}
	else//如果拷贝的目的地址在源地址后面  则源地址从后向前拷贝到目的地址
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)str + count);
		}
	}
	return ret;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HT . WANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值