手写strstr、strcpy、memmove和memcpy

386 阅读4分钟

一、手写strstr

strstr函数是C语言中的一个标准库函数,用于在一个字符串中查找第一个出现的另一个字符串。如果找到,strstr函数将返回指向第一个匹配字符的指针;否则,返回NULL。strstr函数的原型位于string.h头文件中。其函数原型为:

char* my_strstr(const char* str,const char* targrt);

其中,str1是待搜索的字符串,即搜索范围。 str2是要查找的字符串。

代码如下:

  # include <stdio.h>
        char* my_strstr(const char* str,const char* targrt);
        int main()
        {
            char* s = "this is an example";
            char* target1 = "is";
            char* target2 = "exa";
            char* res1 = my_strstr(s,target1);
            char* res2 = my_strstr(s,target2);
            printf("res1 = : %s\n",res1);
            printf("res2 = : %s\n",res2);
            return 0;
        }
        //const char * c代表的是,指针c指向的内存的内容是不可以改变的,但是指针c是可以改变的。如果是char *const c,那么c指向的内容可以改变,但是c不能改变,即不能++和--
        char* my_strstr(const char* str,const char* target)//在str中找到目标字符串target,如果找到,就返回指向匹配字符串的第一个位置,否则返回null
        {
            if(*target == '\0')
                return (char*)str;//需要类型转换一下
            while(*str != '\0')
            {
                char* ch1 = str;
                char* ch2 = target;
                while(*ch1 != '\0' && *ch2 != '\0' && *ch1 == *ch2)
                {
                    ch1 ++;
                    ch2 ++;
                }
                if(*ch2 == '\0')
                    return (char*)str;
                str ++;
            }
            return NULL;
        }

二、手写strcpy

strcpy函数是C语言中的一个标准库函数,用于将一个字符串(以'\0'为结束符)复制到另一个字符串。strcpy函数的原型位于string.h头文件中。

# include <stdio.h>
    char* my_strcpy(char* destination,const char* sourse);
    int main()
    {
        char des[40];
        char s[] = "this is an example";
        my_strcpy(des,s);
        printf("des = : %s\n",des);
        printf("s = : %s\n",s);
        return 0;
    }
    char* my_strcpy(char* destination,const char* sourse)
    {
        if(destination == NULL || sourse == NULL)
            return destination;
        char* des = destination;
        while(*sourse != '\0')
        {
            *(des ++) = *(sourse ++);
        }
        *des = '\0';
        return destination;
    }


三、手写memmove

memmove和memcpy的功能类似,都是把一段内存段的内容复制到另一段内存区域。

只不过memmove可以处理源内存和目标内存区域重合的情况。

image.png

手写代码如下:


# include<stdio.h>
# include<stddef.h>//包含size_t
void *my_memmove(void* destination,const void *sourse,size_t num);
int main()
{
    char s[] = "this is an example";
    printf("s = : %s\n",s);

    my_memmove(s + 4,s,3);//将s字符串的前3个字符,向后移动4个位置
    printf("s = : %s\n",s);
    //size_t i = 0;
    //i --;
    //printf("i = : %lu\n",i);//为了测试size_t数据类型的最大值。size_t数据类型是无符号的,减到0之后再往下减就会减到他的最大值,然后循环
    return 0;
}
void *my_memmove(void* destination,const void *sourse,size_t num)
{
    char *des = (char*)destination;//因为要一个字节一个字节的复制,char正好是一个字节,所以刚刚好
    const char* src = (const char*)sourse;

    //下面要处理可能的内存重叠情况,即des和src的内存有可能是部分重叠的,这样在复制的过程中就要特别注意顺序,不然有可能会把数据覆盖掉.这部分画个图就明白了
    //des地址比src小,在src的前面,这个时候按照地址从小到大复制
    if(des < src)
    {
        for(size_t i = 0;i < num;++ i)
        {
            des[i] = src[i];
        }
    }else{
        
        for(size_t i = num;i > 0;-- i)
        {
            des[i - 1] = src[i - 1];
        }
        /*
        //下面这种写法会内存越界
        for(size_t i = num - 1;i >= 0;-- i)//size_t数据类型是无符号的,减到0之后再往下减就会减到他的最大值,然后循环,然后就越界了
        {
            des[i] = src[i];
        }*/
    }
    return destination;
}

memmove函数返回指向目标区域内存的指针。

结果:

image.png

memmove的关键就在于处理内存重叠,也就是需要判断源地址和目标地址之间的大小关系,谁的地址更大。

四、手写memcpy

memcpy和memmove的区别就在于,memmove可以防止内存覆盖,即源内存区域和目标内存区域有重叠的情况。而memcpy的话,需要我们手动保证内存不会有重叠,否则会产生未定义的行为。


# include<stdio.h>
# include<stddef.h>//包含size_t
//#include <iostream>
#include <string.h>
void *my_memcpy(void *destination,const void *sourse,size_t num);
int main()
{
    char str[] = "this is an example";
    printf("str的长度是: %d\n",strlen(str));//不包含'\0'结束符
    printf("size_of str : %d\n",sizeof(str));//包含'\0'结束符
    char des[30];
    my_memcpy(des,str,strlen(str));
    printf("str : %s\n",str);
    printf("des : %s\n",str);
    return 0;
}
void *my_memcpy(void *destination,const void *sourse,size_t num)
{
    char* des = (char*)destination;
    const char* src = (const char*)sourse;
    for(size_t i = 0;i < num;i ++)
    {
        des[i] = src[i];
    }
    return destination;
}

在上面的代码中,我们之所以要将 void 类型指针转换为 char 类型指针,原因在于 void 类型不可知,无法对其进行解引用。

而我们要提取每个字节的值,然后逐个拷贝,最简单的就是找一个一个字节大小的数据类型,那么就是 char。