string函数的实现

我们在学C语言的时候,用了很多的处理字符和字符串的库函数,今天我们就自己实现一下这些和字符串相关的库函数。

1.求字符串的长度(strlen)

字符串是以’\0’作为结束标志的,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包括’\0’)。
这里注意:strlen的返回值是size_t无符号的。
下面代码用三种方式实现了strlen:

//借助指针
size_t my_strlen3(char* str)
{
    if(str == NULL)
    {
        return -1;
    }
    if(*str == '\0')
    {
        return 0;
    }
    char* p_str = str;
    while(*p_str)
    {
        p_str++;
    }
    return p_str - str;
}
//递归
int my_strlen2(char* str)
{
    if(*str == '\0')
    {
        return 0;
    }
    return 1 + my_strlen2(str+1);
}
//借助计数的方式
int my_strlen(char* str)
{
    int count = 0;
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}
2.字符串拷贝(strcpy)

strcpy拷贝的时候会将源字符串的’\0’也拷到目标空间,并且源字符串不能修改,将其定义为const。
下面代码的两种写法都可以:

char* my_strcpy(char* dst,const char* src)
{
    if(dst == NULL || src == NULL)
    {
        return NULL;
    }
    while(*dst++ = *src++)
    {
        ;
    }
    return dst;
 //   while(*src)
 //   {
 //       *dst = *src;
 //       dst++;
 //       src++;
 //   }
 //   *dst = '\0';
}
3.字符串拼接(strcat)

将源字符串追加到目标字符串的后面,源字符串不能被修改。
代码如下:

//字符串拼接
char* my_strcat(char* dest,const char* src)
{
    if(src == NULL && dest == NULL)
    {
        return NULL;
    }
//    if(dest == NULL)
//    {
//        return src;
//    }
    if(src == NULL)
    {
        return dest;
    }
    char* p_dest = dest;
    while(*p_dest)
    {
        p_dest++;
    }
    while(*p_dest++ = *src++)
    {
        ;
    }
    return dest;

}
4.字符串比较(strcmp)

优先考虑AscII码,然后考虑字符串长度。
标准规定:
1)第一个字符串大于第二个字符串,则返回大于0的数;
2)第一个字符串等于第二个字符串,则返回0;
3)第一个字符串小于第二个字符串,则返回小于0的数。

代码实现如下,源字符串和目标字符串都不能被修改:

//字符串比较
int my_strcmp(const char* dest,const char* src)
{
    assert(dest != NULL);
    assert(src != NULL);
    int ret = 0;
    while(!(ret = *(unsigned char*)dest - *(unsigned char*)src) && *dest)
    {
        src++;
        dest++;
    }
    //char默认是有符号的,所以要将它强转为无符号的,不然会出错
    //比如129 > 48,char占一个字节,有符号范围为 -128 ~ 127,129会被看成负数,因此 < 48
    if(ret == 0)
    {
        return 0;//相等
    }
    else if(ret > 0)
    {
        return 1;
    }
    else
    {
        return -1;

    }

}
5.在一个字符串中查找子串(strstr)

返回str2第一次出现在str1中的位置以后的字符,两个字符串都不能被修改。
代码如下:

char* my_strstr(const char* str1,const char* str2)
{
    if(str1 == NULL ||str2 == NULL)
    {
        return NULL;
    }
    if(*str2 == '\0')
    {
        return NULL;
    }
    /定义三个指针
    char* start =(char*) str1;
    char* p_str2 = (char*)str2;
    char* p_str1 = NULL;
    while(*start)
    {
        //p_str1和p_str2用来遍历两个字符串
        p_str1 = start;;
        p_str2 = (char*)str2;
        while((*p_str1)&&(*p_str2)&&(*p_str1 == *p_str2))
        {
        /两个字符串相等
            p_str1++;
            p_str2++;
        }
        if(*p_str2 == '\0')
        {
           return start;
        }
        start++;
    }
    return NULL;

}
6.逐字节拷贝(memcpy)

函数原型:void* memcpy(void* destination,const void* srouce,size_t num);
1)函数memcpy从srouce的位置开始向后复制num个字节的数据到destination;
2)函数遇到’\0’不会停止;
3)如果source和destination有任何的重叠,复制的结果都是未定义的。
代码实现如下:

//按字节拷贝,遇到\0不会停止
void *my_memcpy(void* dest,const void* src,size_t count)
{
    assert(dest);
    assert(src);

    char* p_src = (char*)src;
    char* p_dest = (char*)dest;
    while(count)
    {
        *p_dest = *p_src;
        p_dest++;
        p_src++;
        count--;
    }
    return dest;

}
7.memmove

我们之前的拷贝都是一个一个从前往后拷,可是如果内存重叠了会出现什么现象?
这里写图片描述
上图给出了字符串拷贝时的四种情况,1)和 2)没有内存重叠,3)和 4)是内存重叠的情况。

就拿4)来说,我们还是按照以往的从前往后拷贝,将src中的元素一个一个拷给dst,这时新拷贝的元素就会覆盖掉src中还没有拷给dst的内容,就会出现错误。所以,从后往前拷贝就避免了这个问题。

具体采用哪种拷贝方式,看情况:1)、2)、3)采用从前往后拷贝,4)从后往前拷贝。
与类型无关。函数原型如下:
void * memmove(void*destination,const void* source,size_t num);
和memcpy的差别就是memmove函数处理的源代码和目标内存块是可以重叠的。

//按字节拷贝,有内存重叠问题
void* my_memmove(void* dest,void* src,int count)
{
    assert(dest);
    assert(src);

    char* p_src = (char*)src;
    char* p_dest = (char*)dest;

    //内存重叠:src < dest && dest < src+count,从后往前拷
    if(p_src < p_dest && p_dest < p_src+count)
    {
        p_src = (char*)src + count -1;
        p_dest = (char*)dest + count -1;
        while(count--)
        {
            *p_dest = *p_src;
            p_dest++;
            p_src++;
        }
    }
    //两种情况:都从前往后拷
    //有内存重叠的情况:dest<src
    //没有内存重叠的情况
    else
    {
        while(count--)
        {
            *p_dest = *p_src;
            p_dest++;
            p_src++;
      }
    }
    return dest;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值