• C语言字符串函数和内存函数的介绍与模拟实现


    0.前言

    C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数.

    1.字符串函数介绍与模拟实现

    • C语言本身就带有一些库函数,所以看见别人不认识的函数可以在这个网站查一查,使用库函数记得引相应的头文件

      [Reference - C++ Reference]:

    1.1 strlen

    求字符串的个数的函数

    例如

    int main()
    {
        char arr1[] = "abcdef";
        int ret = strlen(arr1);
        printf("%d\n", ret);
        return 0;
    }

    运行结果

     

    size_t strlen ( const char * str );

    • 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。

    • size_t strlen ( const char * str );

    • 参数指向的字符串必须要以 '\0' 结束。

    • 注意函数的返回值为size_t,是无符号的

    • 自己模拟实现

    // 计算器的方式实现
    size_t my_strlen(const char* str)
    {
        int count = 0;
        while (*str != '\0')
        {
            count++;// 统计'\0'前面的字符的个数
            str++;
        }
        return count;
    }
    // 递归的方式实现
    size_t my_strlen(const char* str)
    {
        if (*str == '\0')
            return 0;
        else
            return 1 + my_strlen2(str + 1);
    }

    1.2 strcpy

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

    字符串拷贝函数,给你两个字符串可以,可以把一个字符串放到另外一个空间里

    例如

    int main()
    {
    ​
        char arr1[] = "hello world!";
        char arr2[20] = { 0 };
        strcpy(arr2, arr1);
        printf("%s\n", arr2);
        return 0;
    }
    ​

    运行结果

     

    • 源字符串必须以'\0',作为字符结束标志

    • 会将源字符串的'\0'拷贝放到目标空间中

    • 目标空间必须是可变的

    • 目标空间的空间必须足够大,以确保能放到目标空间中

    • 自己模拟实现

    strcpy函数的模拟实现

    #include 
    char* my_strcpy(char* dest, const char* src)
    {
        assert(dest && src);
    ​
        char* ret = dest;
        while (*dest != *src)
        {
            *dest = *src; // 把src的字符赋给dest
            dest++;
            src++;
        }
    ​
        return ret;
    }

    其实我们理解了while循环的判断还可以把代码变得更简单,直接在while循环的判断部分把src的值直接赋值给dest.

    当src的值找到'\0'了并且赋给了dest后,while循环就会停下来,就把src里的全部字符拷贝放到dest里面

    char* my_strcpy(char* dest, const char* src)
    {
        assert(dest && src);
    ​
        char* ret = dest;
        while (*dest++ = *src++)
        {
            ;
        }
    ​
        return ret;
    }

    1.3 strcat

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

    字符串追加函数,可以在一个字符串里在追加另外一个字符串

    例如

    int main()
    {
        char arr1[20] = "hello ";
        char arr2[] = "world!";
        strcat(arr1, arr2);
        printf("%s\n", arr1);
    ​
        return 0;
    }

    运行结果

    • 源字符串必须以 '\0' 结束。

    • 目标空间必须有足够的大,能容纳下源字符串的内容。

    • 目标空间必须可修改。

    • 字符串自己给自己追加可以吗?

    • 答案是不可以的,因为自己给自己追加会把'\0'覆盖,'\0'是字符串结束的标志,把'\0'覆盖追加就停不下来了

    strcat的模拟实现

    char* my_strcat(char* dest, const char* src)
    {
        assert(dest && src);
        char* ret = dest;
        // 找到目标的'\0'
        while (*dest != '\0')
        {
            dest++;
        }
        // 拷贝到目标空间
        while (*dest++ = *src++)
        {
            ;
        }
        
        return ret;
    }

    1.4 strcmp

    int strcmp ( const char * str1, const char * str2 );

    字符串比较函数,如果arr1里的字符大于arr2里的字符返回>0的数,如果arr1里的字符小于arr2里的字符返回<0的数,

    如果arr1里的字符等于arr2里的字符返回=0的数.

    例如

    int main()
    {
        char arr1[] = "abcd";
        char arr2[] = "abc";
        int ret = strcmp(arr1, arr2);
        printf("%d\n", ret);
        return 0;
    }

    执行结果

     

     

    标准规定

    • 第一个字符串大于第二个字符串,则返回大于0的数字

    • 第一个字符串等于第二个字符串,则返回0

    • 第一个字符串小于第二个字符串,则返回小于0的数字

    strcmp的模拟实现

    int my_strcmp(const char* str1, const char* str2)
    {
        assert(str1 && str2);
        while (*str1 == *str2)
        {
            if (*str1 == '\0')
               return 0;
            str1++;
            str2++;
        }
        
        if (*str1 > *str2)
            return 1;
        else
            return -1;
    ​
        return *str1 - *str2;
    }

    1.5 strncpy

    char * strncpy ( char * destination, const char * source, size_t num );

    跟strcpy函数的功能类似,n表示的是num,可以指定拷贝字符串的个数

    例如

    int main()
    {
    ​
        char arr1[] = "hello world!";
        char arr2[20] = { 0 };
        strncpy(arr2, arr1,5);
        printf("%s\n", arr2);
        return 0;
    }

    执行结果

     

    char * strncat ( char * destination, const char * source, size_t num );
    • 拷贝num个字符从源字符串到目标空间中

    • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

    1.6 strncat

    char * strncat ( char * destination, const char * source, size_t num );

    跟strcat的功能类似,n也表示num,追加num个字符到目标空间

    例如

    int main()
    {
        char arr1[20] = "hello ";
        char arr2[] = "world!";
        strncat(arr1, arr2,6);
        printf("%s\n", arr1);
    ​
        return 0;
    }

    执行结果

     

    1.7 strncmp

    int strncmp ( const char * str1, const char * str2, size_t num );

    跟strcmp的功能类似,可以指定比较num个字符

    例如

    int main()
    {
        char arr1[] = "abcd";
        char arr2[] = "abce";
        int ret = strncmp(arr1, arr2,4);
        printf("%d\n", ret);
        return 0;
    }

    执行结果

     

    int strncmp ( const char * str1, const char * str2, size_t num );
    • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

    1.8 strstr

      char * strstr (const char * str1, const char * str2 );

    查找子串的函数,如果存在子串会把整个字符串全部打印出来

    例如

    int main()
    {
        char arr1[] = "abcdefg";
        char arr2[] = "abc";
    ​
        char* p = strstr(arr1, arr2);
        if (*p == NULL)
        {
            printf("找不到\n");
        }
        else
        {
            printf("%s\n", p);
        }
    ​
        return 0;
    }

    执行结果

     

    strstr函数的模拟实现

    char* my_strstr(const char* str1, const char* str2)
    {
        assert(str1 && str2);
        char* s1 = NULL;
        char* s2 = NULL;
        char* cp = str1;
        while (*cp)
        {
            s1 = cp;
            s2 = (char*)str2;
            while (*s1 && *s2 && *s1 == *s2)
            {
                s1++;
                s2++;
            }
            if (*s2 == '\0')
            {
                return cp;
            }
            cp++;
        }
        return NULL;
    }

    1.9 strtok

    char * strtok ( char * str, const char * delimiters );
    • delimiters是分割符,把长的字符串按照分隔符分成多段短的字符串

    • delimiters的参数含有0个或者多个分割符

    • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。

    • (注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)

    • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。

    • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。

    • 如果字符串中不存在更多的标记,则返回 NULL 指针。

    将长的字符串按照分割符分成多个子串

    例如

    int main()
    {
        char arr[] = "192.168.222.33";
        char buf[30] = { 0 };
        strcpy(buf, arr);
        const char* p = ".";
        char* str = strtok(buf, p);
        printf("%s\n", str);
    ​
        str = strtok(NULL, p);
        printf("%s\n", str);
    ​
        str = strtok(NULL, p);
        printf("%s\n", str);
    ​
        str = strtok(NULL, p);
        printf("%s\n", str);
        return 0;
    }

    执行结果

     

    2.0 strerror

    错误码返回信息函数,这个函数只要你给他一个错误码,它就能返回一个错误信息

    例如

    int main()
    {
        char* str = strerror(0);
        printf("%s\n", str);
    ​
        str = strerror(1);
        printf("%s\n", str);
    ​
        str = strerror(2);
        printf("%s\n", str);
    ​
        str = strerror(3);
        printf("%s\n", str);
    ​
        str = strerror(4);
        printf("%s\n", str);
        return 0;
    }

    执行结果

     

    2.1 memcpy

    void * memcpy ( void * destination, const void * source, size_t num );
    • memcpy函数从source的位置向后复制num字节的数据放到destination的位置

    • 这个函数在遇到'\0'的时候不会停下来

    • 如果source和destination有任何重叠,复制的结果是不可知的

    内存拷贝函数,有两块空间,可以把一块的空间里的内容放到另外一块空间里.

    例如

    int main()
    {
        int arr1[] = { 1,2,3,4,5,6,7,8 };
        int arr2[10] = { 0 };
        memcpy(arr2, arr1,32);
        int i = 0;
        int sz = sizeof(arr1) / sizeof(arr1[0]);
        for (i = 0; i < sz; i++)
        {
            printf("%d ", arr2[i]);
        }
    ​
        return 0;
    }

    执行结果

     

    这里把arr1数组的内容放到arr2数组里面

    memcpy函数的模拟实现

    void* my_memcpy(void* dest, const void* src, size_t num)
    {
        assert(dest && src);
        void* ret = dest;
    ​
        while (num--)
        {
            *(char*)dest = *(char*)src;// 把dest强转(char*)然后进行解引用,同理src也是
            dest = (char*)dest + 1;// 把dest原来的位置往后挪
            src = (char*)src + 1; //  把src原来的位置往后挪
        }
        return ret;
    }

    2.2 memmove

    void * memmove ( void * destination, const void * source, size_t num );
    • memcpy和memmove的区别在于memmove函数处理源内存块和目标空间内存块可以重叠

    • 如果源内存块和目标空间内存块重叠,就需要用memmove函数

    例如

    int main()
    {
        int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
        memmove(arr1, arr1+2, 12);
        int i = 0;
        int sz = sizeof(arr1) / sizeof(arr1[0]);
        for (i = 0; i < sz; i++)
        {
            printf("%d ", arr1[i]);
        }
        return 0;
    }

    把arr1向后12字节的内容,放到arr1里,就是把4,5,6覆盖1,2,3,然后后面的内容不变

    执行结果

     

    memmove函数的模拟实现

    void* my_memmove(void* dest, const void* src, size_t num)
    {
        assert(dest && src);
        void* ret = dest;
        if (dest < src)
        {
            while (num--)
            {
                // 前往后
                *(char*)dest = *(char*)src;
                dest = (char*)dest + 1;
                src = (char*)src + 1;
            }
        }
        else
        {
            // 后往前
            while (num--)
            {
                *((char*)dest + num) = *((char*)src + num);
            }
        }
        return ret;
    }

    这个分两种情况,当dest后进行拷贝,当dest>src时,是后->前。这里比较的是位置,通过画图可以看得出

    dest

     

    dest>src

     

    2.3 memcmp

    int memcmp ( const void * ptr1, const void * ptr2, size_t num );
    • 比较从ptr1和ptr2指针开始的num个字节

    • 返回值如下

     

    使用例子

    int main()
    {
        int arr1[] = { 1,2,3,4,6 };
        int arr2[] = { 1,2,3,4,5 };
        int ret = memcmp(arr1, arr2, 17);
        printf("%d\n", ret);
    ​
    }

    执行结果

     

    这里就不带大家模拟实现的,感兴趣的可以自己研究。

  • 相关阅读:
    Python编程——模块、包和__init__.py
    1024 蓝屏漏洞攻防战(第十九课)
    什么是Java中的反射(Reflection),如何使用它
    harbor 只读模式修改
    uniapp的表单验证
    Workerman开启ssl方法如下
    工作积累——JPA事务中数据更新后查询结果为旧数据的问题
    超级棒,使用 LIME 和 SHAP 可轻松解释机器学习模型的预测
    第九章、类的生命周期
    Python学习六:模块
  • 原文地址:https://blog.csdn.net/weixin_64214213/article/details/130833139