2.库函数的模拟实现
目录
以下三个需轻松手撕:
1. memcpy
2. memmove
3. strstr
下面三个出现的相对较少:
1.strlen
2. strcpy
3. strcmp
以下三个需轻松手撕:
1. memcpy
void *my_memcpy(void *dest, const void *src, size_t count)函数memcpy从src的位置开始向后复制count个字节的数据到dest位置。这个函数在遇到 '\0' 的时候并不会停下来。如果src和dest有任何重叠,复制结果都是未定义的。
-  dst: 目标内存区域的指针,表示数据将要被复制到的位置。
-  src: 源内存区域的指针,表示数据来源的位置。
-  count: 要复制的字节数。
首先要有一个返回变量 ret,保存目标内存区域的起始地址,因为后面会对 dst++,防止这个位置丢失。
然后断言 dst 和 src 都不为空。
然后开始循环复制count个字节,也就是走 count 步,while(count--)。
在循环中,要将src指向的内容复制到dst中,然后各自移向下个字节。
void *my_memcpy(void *dst, const void *src, size_t count)
{
    void* ret = dst;
    assert(dst && src);
    while (count--)
    {
        *(char*)dst = *(char*)src;
        dst = (char*)dst + 1 ;
        src = (char*)src + 1;
    }
    return ret;
}- 由于 void*类型不能直接解引用,因此需要将其强制转换为char*类型(因为char类型的大小为一个字节,适合逐字节复制)。
-  void*不能直接进行指针算术运算(如+或-),因为void类型没有大小。
-  必须将其转换为具体类型的指针(如 char*)才能进行指针算术运算。
为什么 dst 和 src 是 void* 类型?
-  void*是一种通用指针类型,可以指向任何类型的数据(如char、int、struct等)。
-  通过使用 void*,my_memcpy函数可以处理任意类型的内存区域,而不需要为每种数据类型编写单独的函数。
返回 void* 类型的设计使得函数可以支持链式调用。
2. memmove
void * memmove(void* dst, const void* src, size_t count)- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
- 如果源空间和目标空间出现重叠,就得使用memmove进行处理
-  复制的源区间是 [src, src + count)
-  复制的目标区间是 [dst, dst + count)
如何实现可以重叠:分情况讨论,一种就是没有重叠正常处理,另一种是重叠了进行特殊处理。
void *ret = dst;  保存目标内存区域的起始地址,用于返回1. 检查内存区域是否重叠:
if (dst <= src || (char*)dst >= ((char*)src + count))-  void*指针之间可以直接进行比较(如==,!=,<,<=,>,>=),因为比较的是指针的地址值,而不是指针所指向的数据。所以dst <= src 可以不加char*。
-  如果涉及运算,必须将 void*转换为具体类型的指针(如char*)才能进行算术运算。
-  dst <= src:如果目标内存区域的起始地址小于或等于源内存区域的起始地址,说明没有重叠,或者重叠部分不会影响复制。如下例子,不管count多大都不会影响复制。
    src
     |
 a b c d e f g
 |
dst-  (char*)dst >= ((char*)src + count):如果目标内存区域的起始地址大于或等于源内存区域的结束地址(即src + count),说明没有重叠。如下例,例如 count = 2,不影响复制。
  src src+count
   |   |
 a b c d e f g
         |
        dst如果没有重叠或重叠不影响复制:
while (count--)
{
    *(char*)dst = *(char*)src;
    dst = (char*)dst + 1;
    src = (char*)src + 1;
}2.如果重叠了并且会影响复制怎么办:
比如这种情况:dst > src 并且dst < src+count。
src src+count
 |     |
 a b c d e f g
     |     |
    dst dst+count如果还和上面一样进行复制,c变成a,d变成b,e就变成a了,我们想要得复制结果是e是c,因为重叠影响了复制,导致内容被覆盖了。
需要从高地址到低地址逐字节复制,以避免覆盖源内存区域的数据。
如果我们找到dst的末尾位置,从末尾逐步向前复制就可以解决这个问题。
先把c赋给e,dst 和 src 各自减一,b赋给d,a赋给c,同样是进行count步。
void * memmove(void *dst, const void* src, size_t count)
{
    void *ret = dst;
    if(dst<=src || (char*)dst >= ((char*)src+count))
    {
        //低到高
        while (count--)
        {
            *(char*)dst = *(char*)src;
            dst = (char*)dst + 1 ;
            src = (char*)src + 1;
        }
    }
    else
    {
        //把指针放到高处
        dst = (char*)dst + count-1;
        src = (char*)src + count-1;
        //高到低
        while (count--)
        {
            *(char*)dst = *(char*)src;
            dst = (char*)dst - 1 ;
            src = (char*)src - 1;
        }
    }
    return ret;
}3. strstr
用于在字符串 str1 中查找子字符串 str2 的第一次出现位置。如果找到,返回 str2 在 str1 中的起始地址;如果找不到,返回 NULL。
const char* my_strstr(const char* str1, const char* str2)
{
    const char* s1 = NULL;
    const char* s2 = NULL;
    const char* cur = str1;-  str1是主字符串,str2是要查找的子字符串。
-  s1和s2是临时指针,用于在查找过程中遍历str1和str2。
-  cur是当前查找的起始位置,初始化为str1的起始地址
if (*str2 == '\0')//特殊场景
    return str1;- 如果 str2是空字符串(即*str2 == '\0'),则直接返回str1,因为空字符串是任何字符串的子字符串。
    while (*cur)
    {
        s1 = cur;
        s2 = str2;
        while (*s1 && *s2  && *s1 == *s2)
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
            return cur;
        cur++;
    }循环遍历 str1,直到 cur 指向的字符为 '\0'(即字符串结束)。
如果cur++,说明从当前cur开始没找到str2,所以去下一个位置做开始进行寻找。每次循环开始时,s1 被设置为 cur,s2 被设置为 str2,准备开始新一轮的匹配
-  如果 *s1和*s2相等且都不为'\0',则继续比较下一个字符。
-  如果 *s1或*s2为'\0',或者字符不相等,则退出循环。
-  只有由于 str2 遍历完导致退出循环这种情况下,才说明str1中是有str2的,开始位置就是cur -  如果*s1是'\0',说明str2还没走完,str1已经走完了,一定不符合 
-  如果字符不相等,那cur去下个位置,s2回到str2开头重新寻找 
 
-  
最后如果str1遍历完都没能返回结果,那就说明不存在,返回NULL。
const char* my_strstr(const char* str1, const char* str2)
{
    const char* s1 = NULL;
    const char* s2 = NULL;
    const char* cur = str1;
    assert(str1 && str2);
    if (*str2 == '\0')//特殊场景
        return str1;
    while (*cur)
    {
        s1 = cur;
        s2 = str2;
        while (*s1 && *s2  && *s1 == *s2)
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
            return cur;
        cur++;
    }
    return NULL;
}
下面三个出现的相对较少:
1.strlen
int my_strlen1(const char* str)
{
    int count = 0;
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}
int my_strlen2(const char* str)
{
    //不能创建临时变量
    if(*str == '\0')
        return 0;
    return 1 + my_strlen2(str+1);
}2. strcpy
char *my_strcpy(char *dst, const char *src)
{
    char *ret = dst;
    assert(dst && src);
    while((*dst++ == *src++))
        ;
    return ret;
}3. strcmp
int strcmp(const char *str1, const char *str2)
{
    while(*str1 == *str2)
    {
        if(*str1 == '\0')
            return 0;
        str1++;
        str2++;
    }
    if(*str1 > *str2)
        return 1;
    else
        return -1;
}