• 模拟实现memcpy和memmove


    目录

    前言:

    memcpy:

    介绍:​编辑

    模拟实现memcpy:

    memmove:

    介绍:

    模拟实现memmove:

    总结:


    前言:

    在上一篇blog中,我们探讨了有关strcpy和strcmp等的字符串函数和对其的模拟实现。

    今天,我们着重讨论有关memcpy和memmove这两种内存函数,并模拟实现它们。

    首先我们要知道内存函数和字符串函数的区别,

    字符串函数像strcpy和ctrcmp的使用对象都为字符串,而面对整形或者浮点型就不适用了,因此我们在这里引出了内存函数。

    内存函数像memcpy和memmove等的使用对象是内存中的各个数据,不仅仅可以对于字符串还可以对整形数组或者字符数组进行操作。接下来我们来介绍这两个函数。

    memcpy:

    介绍:

    对于memcpy它的代码如下:

    void* memcpy(void* destination, const void* source, size_t num)

    destination----为需要将源头拷贝到的区域。

     source——为源头将其拷贝到destination。

    num——为单位,表示要拷贝多少个字节到destination。

    具体操作如下:

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. #include
    4. int main()
    5. {
    6. int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    7. int source[] = { 9, 10 };
    8. memcpy(destination, source, 8);
    9. int sz = sizeof(destination) / sizeof(destination[0]);
    10. for (int i = 0; i < sz; i++)
    11. {
    12. printf("%d ", destination[i]);
    13. }
    14. return 0;
    15. }

    我们预期输出9, 10, 3, 4, 5, 6, 7, 8

    实际输出结果与预期结果一致。

    接下来我们来谈谈怎么模拟实现memcpy

    模拟实现memcpy:

    在刚刚的介绍中,我们知道memcpy的表达式是

    void*memcpy(void* destination, const void* source, size_t num)

    我们需要知道他们各个参赛的返回类型,基本都为void*,那么实现他们就和实现qsort如出一辙。

    代码如下:
     

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. #include
    4. #include
    5. void* my_memcpy(void* destination, const void* source, size_t num)
    6. {
    7. assert(destination && source);
    8. void* ret = destination;
    9. while (num--)
    10. {
    11. *(char*)destination = *(char*)source;
    12. destination = (char*)destination + 1;
    13. source = (char*)source + 1;
    14. //注意不可进行(char*)destionation++,因为强制类型转换是临时的,强制类型转换完使用后就没了,这样在++就是对void*加加了,
    15. //而void*是不可以进行++操作的。
    16. }
    17. return ret;
    18. }
    19. int main()
    20. {
    21. int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    22. int source[] = { 9, 10 };
    23. //memcpy(destination, source, 8);
    24. my_memcpy(destination, source, 8);
    25. int sz = sizeof(destination) / sizeof(destination[0]);
    26. for (int i = 0; i < sz; i++)
    27. {
    28. printf("%d ", destination[i]);
    29. }
    30. return 0;
    31. }

    以下代码的操作方式与之前我们实现qsort函数的算法相似,具体可以参考之前的qsort的blog。

    1. *(char*)destination = *(char*)source;
    2. destination = (char*)destination + 1;
    3. source = (char*)source + 1;

    自主实现qsort函数_无双@的博客-CSDN博客

    memmove:

    介绍:

    我们会发现,该函数的各个参数返回类型与memcpy大为相似,其实是的,我们可以将memmove理解为更为完善的memcpy,我们现阶段不需要对他们进行更深的理解。

    什么时候使用memmove呢?

    我们先讲讲my_memcpy的不足之处:

    如果我们需要将destination中的1,2,3,4,5拷贝覆盖到3,4,5,6,7

    预期结果应当为:

    1,2,3,4,5,8

    可如果我们使用my_memcpy会出现一下结果:

    1. void* my_memcpy(void* destination, const void* source, size_t num)
    2. {
    3. assert(destination && source);
    4. void* ret = destination;
    5. while (num--)
    6. {
    7. *(char*)destination = *(char*)source;
    8. destination = (char*)destination + 1;
    9. source = (char*)source + 1;
    10. //注意不可进行(char*)destionation++,因为强制类型转换是临时的,强制类型转换完使用后就没了,这样在++就是对void*加加了,
    11. //而void*是不可以进行++操作的。
    12. }
    13. return ret;
    14. }
    15. int main()
    16. {
    17. int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    18. //int source[] = { 9, 10 };
    19. //memcpy(destination, source, 8);
    20. my_memcpy(destination + 2, destination, 20);
    21. int sz = sizeof(destination) / sizeof(destination[0]);
    22. for (int i = 0; i < sz; i++)
    23. {
    24. printf("%d ", destination[i]);
    25. }
    26. return 0;
    27. }

    最后的输出结果与我们的预期结果不一样。

    图示如此,接下来实现操作。

    当我们拷贝完1,2时,我们即将要拷贝3时,会发现3我们找不到了,3的位置变成了1,4的位置也变成了2.

    所以才会出现重复的1,2,1,2.

    因此面对这种情况,我们可以采取用memmove实现,或者进行逆序排序。如图:

    	memmove(destination + 2, destination, 20);

    再输出结果就会得到与预期一致的数据:

    这就是memmove与memcpy的区别。

    但如果你尝试使用memcpy来进行拷贝,会发现也与memmove一样出现预期结果。

    那是因为VS编译器对memcpy进行了优化。

    接下来我们来讲讲模拟实现memmove


    模拟实现memmove:

    具体代码如下:

    1. #include
    2. void* my_memmove(char* destination, const void* source, size_t num)
    3. {
    4. assert(destination && source);
    5. void* ret = destination;
    6. if (destination < source)
    7. {
    8. //前往后拷贝
    9. for (int i = 0; i < num; i++)
    10. {
    11. *(char*)destination = *(char*)source;
    12. destination = (char*)destination + 1;
    13. source = (char*)source + 1;
    14. }
    15. }
    16. else
    17. {
    18. //从后向前拷贝
    19. while (num--)
    20. {
    21. *((char*)destination + num) = *((char*)source + num);
    22. }
    23. }
    24. return ret;
    25. }
    26. int main()
    27. {
    28. int destination[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    29. //int source[] = { 9, 10 };
    30. //memcpy(destination, source, 8);
    31. memmove(destination + 2, destination, 20);
    32. int sz = sizeof(destination) / sizeof(destination[0]);
    33. for (int i = 0; i < sz; i++)
    34. {
    35. printf("%d ", destination[i]);
    36. }
    37. return 0;
    38. }

    我们来着重分析下伪代码:

    1. void* my_memmove(char* destination, const void* source, size_t num)
    2. {
    3. assert(destination && source);
    4. void* ret = destination;
    5. if (destination < source)
    6. {
    7. //前往后拷贝
    8. for (int i = 0; i < num; i++)
    9. {
    10. *(char*)destination = *(char*)source;
    11. destination = (char*)destination + 1;
    12. source = (char*)source + 1;
    13. }
    14. }
    15. else
    16. {
    17. //从后向前拷贝
    18. while (num--)
    19. {
    20. *((char*)destination + num) = *((char*)source + num);
    21. }
    22. }
    23. return ret;
    24. }

    这一部分是判断destination与source的关系,如果我们此时我的destination小于source,那我们就不能从往前拷贝,而是应当选择从前往后,不然出错原因与上述所讲的一致,会被占用掉。

    实现拷贝也是与qsort函数哪一部分类似,在这里我也不进行多的赘述。

    具体的方法就是将destination和source从void*转换成char*。

     

    接下来就是当destination大于source时,选择的从后往前进行拷贝。

     目前是如图所示:

    当我想要访问source和destination指向的最后一个元素时,可以选择加上sum

    具体解释如图:

    如果我们加上4时,即跳过一个整形,此时指针就会指向:

     就会指向下一个元素的首字节。

    因此我们需要+num再-1,但我们在while语句进行判断的时候,已经实现了num后置--,所以则不需要再进行-1操作。

    如此运行结果为:

    以上就是my_memove的实现过程。

    总结:

     在本文我们主要讲解了memcpy和memmove的内存函数,并模拟实现了这两种函数。

    我们在此学会了可以下来尝试动手自己编写编写。

    这样我们对此会愈发熟悉。

    记住

    “坐而言不如起而行”

    Action speak louder than words!

    若先要看源码可以访问我的Gitee:

    Test_memcpy_memmove_CSDN/Test_memcpy_memmove_CSDN/test.c · 无双/test_c_with_X1 - Gitee.com

  • 相关阅读:
    Oracle/PLSQL: Length Function
    Java学习笔记——并发编程(一)
    windows环境下tensorflow安装
    数字化卡券拉新怎么做?卡券营销系统如何裂变用户
    利用python来打印九九乘法表
    Azure Functions Service Bus Trigger 对容器的支持
    自学黑客(网络安全)技术——2024最新
    智云通CRM:客户说“我们已经有供应商了”,我们该如何回应?
    贼全! 一举通关的 Spring+SpringBoot+SpringCloud 全攻略, 是真香啊
    git bash 常见场景用法
  • 原文地址:https://blog.csdn.net/weixin_72917087/article/details/133126942