模拟实现strcpy,strcat,strstr,strcmp,memcpy,memmove函数

1.实现strcpy :字符串的拷贝

这里写图片描述

//1.参数顺序
//2.函数的功能,停止条件
//3.assert
//4.const修饰可以被改的指针
//5.函数返回值ret保存
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h> //用到system("pause")时

int main ()
{
    char arr[20] = {0}; //目标dest
    char *p = "abcdef"; //源src
    char* my_strcpy(char* dest,const char* src); //函数的声明
    my_strcpy(arr,p);
    printf("%s\n",arr);
}

char* my_strcpy(char* dest,const char* src) 
    //源不需要被改所以加const修饰
    //char* my_strcpy中char*:实现链式访问
{
    char* ret = dest;  //记录初始位置,返回ret 
    assert(dest != NULL);
    assert(src != NULL);
    1.while(*dest++ = *src++)
    {
       ;
    }
    return ret ;
    此处1.相当于
    2.while(*src != '\0')
    {
       *dest = *src; //将src指向的内容拷贝到dest指向的空间里
       src ++;
       dest ++;
    }
    *dest = '\0'; //直到*dest = '\0'为止
    return ret;
}

2.实现strcat:拼接字符串

这里写图片描述

//1.找dest指向的’\0’
//2.拷贝src指向的内容
int main ()
{
   char arr[20] = "hello ";
   char* my_strcat(char*dest , const char* src);
   my_strcat(arr,"word");
   printf("%s\n",arr);
   return 0;
}

char* my_strcat(char*dest , const char* src)
{
    char * ret = dest;
    assert(dest != NULL);
    assert(src != NULL);
    //找dest指向的'\0'
    while (*dest != '\0')
    {
       dest ++;
    }
    //注意此处while不能写成 (*dest++ != '\0')
    //因为永远不能碰到'\0',到\0后又++
    //拷贝src指向的内容
    while(*dest++ = *src++)
    {
        ;
    }
    return ret;
}

需要注意的是:strcat( arr , arr);无法实现,因为第一次遇到’\0’后已经被修改,再也找不到’\0’,进入死循环。

总结strcpy与strcat:都是要找到’\0’ 。strcpy是找src的’\0’,strcat是找arr里dest的’\0’,接着’\0’处拷贝。

3.实现strstr:在一个字符串中找另一个字符第一次出现的位置

这里写图片描述

int main()
{
   char arr[]="abcdefabcdef";
   char* ret = my_strstr(arr,"def");
   if(ret != NULL)
      printf("%s\n",ret);
   else
      printf("不存在");
   return 0;
   system("pause");
}

char* my_strstr(const char* str,const char* substr)
{
   assert(str != NULL);
   assert(substr != NULL);
   if(*substr == '\0')
   {
      return str;
   }
   while(*str)
   {
       const char*s1 = str;
       const char*s2 = substr;
       while(*s1 && *s2 && *s1 == *s2) // *s1和*s2不为'\0'
       {
          s1++;
          s2++;
       }
       if(*s2 == '\0')  //只有当*s2 == '\0'时才算找到并找完了
          return str;   //返回str因为str记录了初始的位置 只有s1和s2在++
       str++;
   }
  return NULL;
}

4.实现strcmp:字符串比较

一直比较到两个字符不相等为止
int main()
{
   char* p = "abcdef";
   char* q = "aaaaaaaaaaaaa";
   //不能直接比较p,q因为其为地址
   char* str1;
   char* str2;
   int my_strcmp(const char* str1,const char* str2);
   int ret = my_strcmp(p,q);
   if(ret < 0)
   {
      printf("p<q");
   }
   else if(ret == 0)
   {
       printf("p == q\n");
   }
   else
   {
      printf("p>q\n");
   }
   return 0;
}

int my_strcmp(const char* str1,const char* str2)
//返回类型为int是由于输出结果为>0<0=0的数
// 但最后优化后结果为p与q的关系
{
   assert(str1 != NULL);
   assert(str2 != NULL);
   while(*str1 == *str2)
   {
      if(*str1 == '\0')  
     //判断两者相等中其中一个为0另外一个也为0
         return 0;
      str1 ++;
      str2 ++;
   }
   return *str1-*str2;  //优化下列代码
   /*if(*str1 > *str2)
      return 1;
   else
      return -1;*/
}

5.实现memcpy:内存拷贝函数(任意类型)

int main()
{
    int i = 0;
    int arr1[10]={0}; //换成float等类型也适用
    int arr2[]={1,2,3,4,5,6,7};//同上
    //无法用strcpy将arr2拷贝到arr1中
    //因为内存中1,2...的存储方式为:01 00 00 00,02 00 00 00
    //strcpy结束的标志为'\0',但是00中的'\0'不是所需的位置
    void* my_memcpy(void* dest,const void* src,size_t count);
    my_memcpy(arr1,arr2,28);
    for (i = 0; i < 7; i++)  
    {  
        printf("%d ", arr1[i]);  
    }  
    system("pause");
    return 0;
}

void* my_memcpy(void* dest,const void* src,size_t count)
//void*为无类型指针,任何类型指针都可赋值
{
    void* ret = dest;
    assert(dest != NULL);
    assert(src != NULL);
    while(count)
    {
        *(char*)dest = *(char*)src; //void*不能强制类型转换,所以类型转换为char*
        //(char*)dest++; //不可以,因为++是对dest作用的
        dest = (char*)dest+1;
        src = (char*)src+1;
        count--; //memcpy结束的标志为count减到0
    }
    return ret;
}

6.实现memmove:内存移动函数

其与memcpy最根本的区别为memmove可以反向拷贝

memcpy的局限性:
举个例子:给上一个数组arr[ ] = 1 2 3 4 5 6 7 8 9,;现在你需要将 3 4 5 6拷贝到 5 6 7 8这几个数的位置去。如果你正序拷贝,你会发现将3拷到5的位置,4拷到6的位置,但是你再继续将5拷贝到7的位置的时候,你会发现5已经被改成了3,你再也无法将5拷贝到7的位置了。
图示说明:
这里写图片描述
首先指针指的是首地址,当指针指向3的时候
这里写图片描述
你准备将3拷贝到5的位置
这里写图片描述
你5拷到7的时候,你会发现5所在的位置,已经被改成了3,这样你再也无法拷贝5这个数字了
这里写图片描述
这样你就会无法实现将3 4 5 6拷贝到 5 6 7 8。
所以,你需要进行反向的拷贝,就是从6开始向前拷贝,先将6拷到8的位置,再将5拷到7的位置,再将4拷到6的位置,再将3拷到5的位置,这样就不会遇到无法完全拷贝的问题。

什么时候需要用memmove?

每当有你需要拷贝的数字的起始位置将要拷贝到你需要拷贝的位置发生了冲突,也就是将要被拷贝到的位置比将要拷贝数字的位置大,这样你就可以反向拷贝了。
图片演示:
这里写图片描述
你会看到,将3 4 5 6拷贝到5 6 7 8的位置,也就是
这里写图片描述
你会发现5所在的位置是大于3所在的位置的,如果是下面这样的
这里写图片描述
将2 3 4 5拷贝到 6 7 8 9的位置,虽然他们也是符合上面的条件,但是你并不需要将其反向拷贝,但是反向拷贝也不会有什么问题,所以,只要满足上面的这个条件,你就可以反向拷贝。
当然,如果他是正向的,也就是不满足上面的这个条件,你就直接拷贝就行了,和memcpy实现的原理一致。

int main()  
{  
    int i = 0;  
    int arr3[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
    void* my_memmove(void* dest, void* src, size_t count);
    my_memmove(arr3 + 5, arr3 + 4, 12);  
    //指把5,6,7拷贝到6,7,8的位置。3个长度由12决定
    for (i = 0; i < 10; i++)  
    {  
        printf("%d ", arr3[i]);  
    }  
    system("pause");  
    return 0;  
} 

void* my_memmove(void* dest, void* src, size_t count)  
{  
    void* ret = dest;  
    char* str1 = (char*)dest;  
    char* str2 = (char*)src;  
    assert(dest != NULL);  
    assert(src != NULL);  
    if (str1 > str2)   
    {  
        while (count--)  
        {  
            *(str1 + count) = *(str2 + count);  
        }  
    }  
    else  
    {  
        while (count--)  
        {  
            *str1++ = *str2++;  
        }  
    }  
    return ret;  
}

猜你喜欢

转载自blog.csdn.net/qq_39378530/article/details/80357772