• 【C语言】内存函数——按字节拷贝


    目录

    一.memcpy函数

    例题

    例1:

    例二:

    模拟实现:

    .memmove函数

    地址重叠

    des>sou

    des<>

    模拟实现

    三.memset函数

    例题:

    使用场景:

    总结:


    一.memcpy函数

    1. void* memcpy(void* destination, const void* source,size_t num);
    2. //传参后使用void*接收,说明这个函数不论是什么类型都会执行。
    • size_t表示无符号数字

    • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

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

    • 如果source和destination有任何的重叠,复制的结果都是未定义的。

    例题

    例1:

    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. return 0;
    7. }

    例二:

    1. int main()
    2. {
    3. float arr1[] = { 1.0f,2.0f,3.0f,4.0f };//数值后加f表示该数为浮点数
    4. float arr2[5] = { 0 };
    5. memcpy(arr2, arr1, 8);
    6. return 0;
    7. }

    模拟实现:

    1. //这种写法不是最好的写发,有些编译器是编不过去的
    2. #include
    3. void* my_memcpy(void* destination, const void* source,size_t num)
    4. {
    5. assert(destination);
    6. assert(source);
    7. void* ret = destination;
    8. for (int i = 0; i < num; i++)
    9. {
    10. *((char*)destination)++ = *((char*)source)++;
    11. }
    12. return ret;
    13. }
    14. //这种方法可以在所以编译器中使用
    15. void my_memcpy(void* destination, const void* source,size_t num)
    16. {
    17. assert(destination);
    18. assert(source);
    19. void* ret = destination;
    20. while(num--)
    21. {
    22. *(char*)destination = *(char*)source;
    23. destination = (char*)destination + 1;
    24. source = (char*)source + 1;
    25. }
    26. return ret;
    27. }
    •  当我们测试source和destination在一个数组并且它们拷贝的范围部分重合时,会发生错误。

    例如使用下面的代码传递:

    1. int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    2. my_memcpy(arr+2, arr, 20);

     内存展示为:

     而编译器定义的memcpy函数却不是这样定义的,memcpy的结果为:

    这并不是说之前写的模拟函数有问题,在这里我们需要明白一点,在C语言规定中,那段错误的结果才是memcpy真实的结果,但我使用的是VS编译器,由于编译器的不同,它将memcpy改的更为强大了而已,其它编译器并非如此。

    在C语言规定中,当拷贝的两组数地址部分重合时,应该使用memmove函数,memmove函数的功能是强于memcpy的,接下来讨论的编译过程也是属于memmove的但在VS编译器当中两者差异不大,这里以C语言规定为准,但大家在平时练习的时候需要注意这一点。

    二.memmove函数

    void* memmove(void* destination,const void* source,size_num);
    • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

    • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

    地址重叠

    当拷贝的两组数出现重叠时,让我们看看memmove函数是如何处理的。

    这个时候我们就要分类讨论了:

    • des == destination

    • sou == source

    • num == 20;

    • 此时des和sou分别是destination和source所表示的地址

    des>sou

    •  如图,当des的地址大于sou时我们就需要从sou最后一位数开始拷贝,这样才能防止被覆盖。

    des

    •  这个时候我们就要从前依次往后拷贝,才能顺利完成拷贝。

    模拟实现

    1. void* my_memmove(void* destination, const void* source, size_t num)
    2. {
    3. void* tem = destination;
    4. if (destination < source)
    5. {
    6. for (int i = 0; i < num; i++)
    7. {
    8. *(char*)destination = *(char*)source;
    9. destination = (char*)destination + 1;
    10. source = (char*)source + 1;
    11. }
    12. }
    13. else
    14. {
    15. destination = (char*)destination + num - 1;
    16. source = (char*)source + num - 1;
    17. for (int i = 0; i < num; i++)
    18. {
    19. *(char*)destination = *(char*)source;
    20. destination = (char*)destination - 1;
    21. source = (char*)source - 1;
    22. }
    23. }
    24. return tem;
    25. }

    三.memset函数

    • 此函数目前我见到使用最多的场景是用来初始化一块连续的空间(可以看作数组)

           在第一次完成这篇博客时,我没想着把这个函数加入其中,我感觉它不是很重要,在今天做题的时候,勾起我对这个memset复杂的回忆。

           之前做题时,创建数组后初始化都是进行循环一个个初始化,一次看题解不经意的一瞥,注意到这个函数,用了以后感觉是真香。但毕竟做题少而且平时练习的时候都是直接给数组初始化为0,不像做题的时要自己malloc一个新的空间。

    void* memset(void* ptr,int value,size_t num);
    • sets the first num bytes of the block of memory pointed by ptr the specified value(interpreted as  an unsigned char).
    • memset函数为内存填充函数
    • num表示填充多少个字节
    • value表示填充的值

    例题:

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

    arr1数组最开始的内存:

    memset后:

    • 将前8个字节全都填充为9 

    使用场景:

    1. int main()
    2. {
    3. int* arr = (int*)malloc(sizeof(int) * 5);//开创了5个int大小的空间
    4. memset(arr, 0, 20);
    5. return 0;
    6. }

    memset前:

    memset后:

    总结:

    • 按找C语言的标准来说,memmove的适用范围要比memcpy的更广
    • 这两个函数是以字节为单位,按字节改变的。
    • memset常用来初始化空间

  • 相关阅读:
    UML依赖关系详解
    typecho 反序列化漏洞复现
    隆云通烟雾传感器
    vscode 访问本地或者远程docker环境
    ssm springboot网络订餐点餐跑腿系统java 小程序025
    剑指 Offer 38. 字符串的排列
    three.js学习-智慧城市
    DDD领域驱动设计-值对象
    day10 分布式缓存
    二、Javascript类型[数组、对象]
  • 原文地址:https://blog.csdn.net/m0_52094687/article/details/126863106