• 字符串函数以及内存函数的模拟实现(超详细,全面理解字符串函数!!!)


    目录

    一、strlen

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

    2.注意strlen函数的返回值为size_t,是无符号的

    3.模拟实现strlen

     二、strcpy

    1.源字符串中的 '\0' 拷贝到目标空间

    2.源字符串必须以 '\0' 结束

    3.目标空间必须足够大,以确保能存放源字符串

    4.模拟实现strcpy

    三、strcat

     1.源字符串必须以 '\0' 结束

    2.模拟实现strcat

    四、strcmp

    1.标准规定

     2.模拟实现strcmp

    五、小结

    六、strncpy

     七、strncat

    八、 strncmp

     九、strstr

    9.1模拟实现strstr

     十、strtok

    十一、strerror

    十二、字符分类函数: 

    十三、memcpy

     1.memcpy:内存拷贝

     2.模拟实现memcpy

    十四、memmove

    十五、memset


     

    一、strlen

    c20c873b137e4818ae3def2c2b3aceaa.png

    strlen:求字符串长度的,统计的是字符串中\0之前出现的字符个数(不包含 '\0' )

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

    1. int main()
    2. {
    3. //a b c \0 d e f \0
    4. char arr[] = "abc\0def";
    5. printf("%d\n", strlen(arr));
    6. return 0;
    7. }

    此结果输出为  3(\0结束

    2.注意strlen函数的返回值为size_t,是无符号

    1. #include
    2. int main()
    3. {
    4. if (strlen("abc") - strlen("abcdef") > 0)
    5. printf(">\n");
    6. else
    7. printf("<\n");
    8. return 0;
    9. }

    77a3017407e44a21bcdcbc27325a9645.png

    对于此代码,大家第一眼看到的肯定是小于,则不然,输出结果为  >

    746486cdd97743a2b9c5cfc1679bf234.png

    size_t,是无符号的整形(无符号减无符号为无符号),不可能返回负数

    3.模拟实现strlen

    1. #include <assert.h>
    2. size_t my_strlen(const char* str)//不会改arr,最好使用const
    3. {
    4. assert(str);//保证str为空指针
    5. const char* start = str;//记录起始位置
    6. const char* end = str;//记录末尾位置
    7. while (*end != '\0')
    8. {
    9. end++;//从a往后走直到指向\0
    10. }
    11. return end - start;//得到指针个数
    12. }
    13. int main()
    14. {
    15. char arr[] = "abcdef";
    16. int len = my_strlen(arr);
    17. printf("%d\n", len);
    18. return 0;
    19. }

    c73f6a15c2cf4631a200f385797d7310.png

     运行结果:

    6bf3c3a45c1c4597aac83f3963b759ba.png

     二、strcpy

    043689f923944de1a09b2c76659174ff.png

     strcpy:字符串拷贝

    375e29b5e4664127a5cebc8a9ba3d866.png

    1.源字符串中的 '\0' 拷贝到目标空间

    1. #include
    2. int main()
    3. {
    4. char arr[10] = "xxxxxxxxxx";
    5. const char* p = "abcdef";
    6. strcpy(arr, p);
    7. printf("%s\n", arr);
    8. return 0;
    9. }

    919c62605acf4172a68d8e28046ff78e.png

    6f30fcab2bde40069fced7284396abe0.png

    2.源字符串必须以 '\0' 结束

    2.1  没有‘ \0 ’

    1. int main()
    2. {
    3. char arr[10] = "xxxxxxxxx";
    4. char arr2[] = { 'b', 'i', 't'};
    5. strcpy(arr, arr2);
    6. printf("%s\n", arr);
    7. return 0;
    8. }

    806fb9c54ee44d68adff248f6056f504.png

     1.2有‘ \0 ’

    1. #include
    2. int main()
    3. {
    4. char arr[10] = "xxxxxxxxx";
    5. char arr2[] = { 'b', 'i','\0', 't'};;
    6. strcpy(arr, arr2);
    7. printf("%s\n", arr);
    8. return 0;
    9. }

    5522a37ee9294d49b926003a759d24c3.png

    3.目标空间必须足够大,以确保能存放源字符串

    1. #include <string.h>
    2. int main()
    3. {
    4. char arr[3] = {0};
    5. char arr2[] = "abcdef";
    6. strcpy(arr, arr2);
    7. printf("%s\n", arr);
    8. return 0;
    9. }

    e52f3885dffa4d519bc5b9032e0f2441.png

    4.模拟实现strcpy

    1. char* my_strcpy(char* dest, const char* src)
    2. {
    3. assert(dest);//断言,保证有效性
    4. assert(src);
    5. char* ret = dest;
    6. while (*dest++ = *src++)
    7. {
    8. ;
    9. }
    10. return ret;
    11. }
    12. int main()
    13. {
    14. char arr1[20] = "abc";
    15. char arr2[] = "hello bit";
    16. printf("%s\n", my_strcpy(arr1, arr2));
    17. return 0;
    18. }

    三、strcat

    fd8b37948c234437b8795e5a66f585f9.png

     strcat:字符追加函数

    1. #include <string.h>
    2. int main()
    3. {
    4. char arr1[20] = "hello ";
    5. char arr2[] = "world";
    6. strcat(arr1, arr2);//字符追加函数
    7. printf("%s\n", arr1);
    8. return 0;
    9. }

    0520f648dd11448985478cbcd3749d19.png

     1.源字符串必须以 '\0' 结束

    1602675a866240ba94c0f47ee4169e2e.png

    2.模拟实现strcat

    1. #include <string.h>
    2. #include <stdio.h>
    3. char* my_strcat(char* dest, const char*src)
    4. {
    5. //1.找目标空间中的\0
    6. char* cur = dest;
    7. while (*cur)
    8. {
    9. cur++;
    10. }
    11. //2.拷贝源头数据到\0之后的空间
    12. while (*cur++ = *src++)
    13. {
    14. ;
    15. }
    16. return dest;
    17. }
    18. int main()
    19. {
    20. char arr1[20] = "hello \0xxxxxxxxxx";
    21. char arr2[] = "world";
    22. printf("%s\n", my_strcat(arr1, arr2));
    23. return 0;
    24. }

     a50033720c4d40dcbd9c49e154cf2750.png

    03bc6c4bfde14a5c8599c87b6412199a.png

    四、strcmp

    1ecd7227b568441d9e0864dc137369f7.png

     strcmp:字符串比较(比较的是对应位置上字符的大小,而非长度必须以 '\0' 结束

    1.标准规定

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

    21fda71342db405a82f6e37ec752d408.png

     

    f108dc48a9d74851aaebd71dfba97ab2.png

     2.模拟实现strcmp

    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <assert.h>
    4. int my_strcmp(const char* s1, const char* s2)
    5. {
    6. assert(s1 && s2);
    7. while (*s1 == *s2)
    8. {
    9. if (*s1 == '\0')
    10. {
    11. return 0;
    12. }
    13. s1++;
    14. s2++;
    15. }
    16. return *s1 - *s2;
    17. }
    18. int main()
    19. {
    20. char arr1[] = "abc";
    21. char arr2[] = "abc";
    22. int ret = my_strcmp(arr1, arr2);
    23. if (ret < 0)
    24. printf("arr1);
    25. else if(ret>0)
    26. printf("arr1>arr2\n");
    27. else
    28. printf("arr1==arr2\n");
    29. printf("%d\n", ret);
    30. return 0;
    31. }

    504c679b081e41a1a06d6b255b16d112.png

    五、小结

    长度不受限制的字符串:

    strcpy

    strcat

    strcmp

    长度受限制的字符串:

    strncpy

    strncat

    strncmp

    六、strncpy

    9835472014894547a40406ea3990f12a.png

     

    1. #include <stdio.h>
    2. #include <string.h>
    3. int main()
    4. {
    5. char arr1[20] = "abcdefghi";
    6. char arr2[] = "xxxx";
    7. strncpy(arr1, arr2, 2);
    8. printf("%s\n", arr1);
    9. return 0;
    10. }

    29e0013b787143bda33a89605adc1532.png

     0ac51cbd7c544bcfb6499d2c8e3c2f29.png

     假设:拷贝大于arr的空间

    8ac4d52afbcb455797a8e9d2e8a38820.png

     七、strncat

    707a6d2c05644433bfeecafe20c7622d.png

    1. #include <stdio.h>
    2. #include <string.h>
    3. int main()
    4. {
    5. char arr1[20] = "abcdef\0qqqqqq";
    6. char arr2[] = "xyz";
    7. strncat(arr1, arr2, 2);
    8. printf("%s\n", arr1);
    9. return 0;
    10. }

    c2dbd3a2f8ef42618744f972901af418.png

     a88ab943680d437cb989f198d4b99c1d.png

    八、 strncmp

     22aa4c1b38274feea2cb4f7c85671fcb.png

    1. #include
    2. #include
    3. int main()
    4. {
    5. int ret = strncmp("abcdef", "abc", 3);
    6. printf("%d\n", ret);
    7. return 0;
    8. }

    f513d798f729456e986295620dee3c8f.png

    假设比较前四个就是d\0比较:

    7535c3a93bae4d6c9394f069601f0de4.png

     九、strstr

    6c9512f2cab84ae7a01fe98b612a22d8.png

    strstr在一个字符串中找另一个字符串是否存在

    存在:返回子串第一次出现的位置

    不存在:返回NULL

    1. int main()
    2. {
    3. char arr1[] = "abcdefabcdef";
    4. char arr2[] = "cdq";
    5. char* p = strstr(arr1, arr2);
    6. if (p == NULL)
    7. {
    8. printf("不存在\n");
    9. }
    10. else
    11. {
    12. printf("%s\n", p);
    13. }
    14. return 0;
    15. }

    757c244d758942e69e70e8e09209346b.png

    9.1模拟实现strstr

    1. char* my_strstr(const char* str1, const char* str2)
    2. {
    3. const char* s1 = str1;
    4. const char* s2 = str2;
    5. const char* p = str1;
    6. if (*str2 == '\0')
    7. {
    8. return str1;
    9. }
    10. while (*p)
    11. {
    12. s1 = p;
    13. s2 = str2;
    14. while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2))
    15. {
    16. s1++;
    17. s2++;
    18. }
    19. if (*s2 == '\0')
    20. {
    21. return (char*)p;//找到了
    22. }
    23. p++;
    24. }
    25. return NULL;//找不到子串
    26. }
    27. int main()
    28. {
    29. char arr1[] = "abcdefabcdef";
    30. char arr2[] = "cdq";
    31. char* p = strstr(arr1, arr2);
    32. if (p == NULL)
    33. {
    34. printf("不存在\n");
    35. }
    36. else
    37. {
    38. printf("%s\n", p);
    39. }
    40. return 0;
    41. }

    81ec5c5e38f443f6a637d2811b12d2a4.png

     十、strtok

    6f4e45a62e3743afa94296d4d4c7e080.png

    sep参数是个字符串,定义了用作分隔符的字符集合
    第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。 

    strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
    strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
    strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
    如果字符串中不存在更多的标记,则返回 NULL 指针。

    1. #include <stdio.h>
    2. #include <stdio.h>
    3. int main()
    4. {
    5. char arr[] = "zpengwei@bitedu.com";
    6. char buf[200] = { 0 };//"zpengwei@bitedu.com"
    7. strcpy(buf, arr);
    8. const char* p = "@.";
    9. char* str = strtok(buf, p);
    10. printf("%s\n", str);
    11. str = strtok(NULL, p);
    12. printf("%s\n", str);
    13. str = strtok(NULL, p);
    14. printf("%s\n", str);
    15. //"@."
    16. //strtok();
    17. //zpengwei
    18. //bitedu
    19. //com
    20. return 0;
    21. }

    b84e5aeb610f40a29a2255d9b51d451c.png

     优化:

    1. #include <stdio.h>
    2. #include <stdio.h>
    3. int main()
    4. {
    5. char arr[] = "zpengwei@bitedu.com";
    6. char buf[200] = { 0 };//"zpengwei@bitedu.com"
    7. strcpy(buf, arr);
    8. const char* p = "@.";
    9. char* str = NULL;
    10. for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p))
    11. {
    12. printf("%s\n", str);
    13. }
    14. return 0;
    15. }

    十一、strerror

    8a1c9d98b3224bcd8d9278bdb74b893b.png

    strerror:把错误码转换成错误信息

    1. #include
    2. #include
    3. int main()
    4. {
    5. printf("%s\n", strerror(0));
    6. printf("%s\n", strerror(1));
    7. printf("%s\n", strerror(2));
    8. printf("%s\n", strerror(3));
    9. printf("%s\n", strerror(4));
    10. return 0;
    11. }

    0e7985d68b7049f69d791e47c95531dd.png

    1. //错误码记录到错误码的变量中
    2. //errno - C语言提供的全局的错误变量
    3. //#include <errno.h>
    4. FILE* pf = fopen("test.txt", "r");
    5. if (pf == NULL)
    6. {
    7. perror("");//打印的依然是errno变量中错误码对应的错误信息
    8. //printf("%s\n", strerror(errno));
    9. return 1;
    10. }
    11. //读文件
    12. fclose(pf);
    13. pf = NULL;
    14. return 0;
    15. }

    十二、字符分类函数: 

    1901844b0e594e04803791f81e676e5f.png

    具体用法可以搜索:cplusplus

    里边都有详细介绍

    十三、memcpy

    一组内存函数:

    memcpy

    memcmp

    memmove

    memset

    09560ba41aa8490bafe1546a0f905205.png

     1.memcpy内存拷贝

    函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
    这个函数在遇到 '\0' 的时候并不会停下来。
    如果source和destination有任何的重叠,复制的结果都是未定义的。

    1. int main()
    2. {
    3. int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    4. int arr2[10] = { 0 };
    5. memcpy(arr2, arr, 20);
    6. //float arr1[] = { 1.0f,2.0f,3.0f,4.0f };
    7. //float arr2[5] = { 0.0 };
    8. //memcpy(arr2, arr1, 8);
    9. return 0;
    10. }

    21a63e31882e46c7b6b3bc69da92af90.png

     2.模拟实现memcpy

    1. // memcpy模拟实现
    2. #include <assert.h>
    3. void* my_memcpy(void* dest, void* src, size_t num)//返回类型 viod*
    4. {
    5. void* ret = dest;
    6. assert(dest);//断言,不能为NULL
    7. assert(src);
    8. while(num--)//一次搞定一个字节,一共num个字节
    9. {
    10. *(char*)dest = *(char*)src;//viod*的指针不能直接引用,强制类型转换成char*的指针
    11. dest = (char*)dest + 1;//同理,强制类型转换成char*的指针
    12. src = (char*)src + 1;//不能用 (char*)src++ :++在外边
    13. }
    14. return ret;//不能返回dest,已经不是起始位置,定义ret
    15. }
    16. int main()
    17. {
    18. int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    19. memcpy(arr1+2, arr1, 20);
    20. memmove(arr1+2, arr1, 20);
    21. int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    22. int arr2[10] = { 0 };
    23. my_memcpy(arr2, arr1, 20);
    24. int i = 0;
    25. for (i = 0; i < 10; i++)
    26. {
    27. printf("%d ", arr2[i]);
    28. }
    29. float arr3[] = { 1.0f,2.0f,3.0f,4.0f };
    30. float arr4[5] = { 0.0 };
    31. my_memcpy(arr4, arr3, 8);
    32. return 0;
    33. }

    9915085cbd624cd4bc7afd027bf4f53d.png

    问题: 把1 2 3 4 5 放到 3 4 5 6 7上,如果使用memcpy时源空间会和目标空间有重合,拷贝的时候可能会把目标空间的一些数据覆盖掉

    454dc4bc0cb247ee87fbda77f13528b2.png

     需要重新换思路

    例如: 

    52528e1a8c554a88be5e07bc751b6a4c.png

    总结:

    cabf749d904042da8cc3990422b862ac.png

     则需要memmove函数

    十四、memmove

    c语言中重叠内存的拷贝是交给:memmove

    1. void* my_memmove(void* dest, void* src, size_t num)
    2. {
    3. void* ret = dest;
    4. assert(dest);
    5. assert(src);
    6. if (dest < src)//1 前->
    7. {
    8. while(num--)
    9. {
    10. *(char*)dest = *(char*)src;
    11. dest = (char*)dest + 1;
    12. src = (char*)src + 1;
    13. }
    14. }
    15. else //2 3 后->
    16. {
    17. while (num--)
    18. {
    19. *((char*)dest + num) = *((char*)src + num);
    20. }
    21. }
    22. return ret;
    23. }
    24. memcpy只需要实现不重叠的拷贝就可以了
    25. memmove是需要实现重叠内存的拷贝的
    26. int main()
    27. {
    28. int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    29. memcpy(arr1+2, arr1, 20);
    30. return 0;
    31. }

    memcpy只需要实现不重叠的拷贝就可以了 
    memmove是需要实现重叠内存的拷贝的

    337a288ca6e54bcea4fff2fb7f08fc92.png

    和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
    如果源空间和目标空间出现重叠,就得使用memmove函数处理。

    04d48c68b09041c6b319b4ab82b6f377.png

    1. int main()
    2. {
    3. int arr1[] = { 1,2,3,0,5 };//01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 00 ..
    4. int arr2[] = { 1,2,3,4,0 };//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ..
    5. int ret = memcmp(arr1, arr2, 13);
    6. printf("%d\n", ret);
    7. return 0;
    8. }

    44a278aec49849f6ba1d18015791c41d.png

    十五、memset

    memset:内存设置

    1f59d0fb41d3463f9f547abc5a4e2b49.png

    6caa353d3e0242cbbdcd8d7488af5dfc.png

    测试:

    1. int main()
    2. {
    3. int arr[] = { 1,2,3,4,5 };
    4. memset(arr, 0, 8);
    5. return 0;
    6. }

    28f07feb02b04a36ad42a3cd8592d41d.png​ 57074be069fb4a1c97336b78b917892e.png

     

     

     

     

     

  • 相关阅读:
    普冉PY32系列(九) GPIO模拟和硬件SPI方式驱动无线收发芯片XL2400
    (39、40)分布式
    智能合约中授权与转账的分离可行性分析
    java基于springboot+vue的高校毕业生求职招聘系统 elementui 前后端分离
    短网址解析长网址python示例
    websocket使用sendObject产生的问题
    JDK动态代理Demo
    关于8月版本UNICHAR的几个问题
    Unity 3D 简易对象池
    【Java】泛型 之 extends通配符
  • 原文地址:https://blog.csdn.net/m0_72161237/article/details/126897136