[C/C++] memcpy 与memmove

[C/C++] memcpy 与memmove

之前曾经有过一次面试,被问到memcpy 与memmove的区别并且手撸一个memmove的实现,当时根本没用过memmove函数,就根据字面意思猜,回答说“memmove用于内存移动,在将源地址数据拷贝到目的地址后,源地址数据会被自动删除……”,后面一查尴尬的一批。。。。
Anyway,现在重新整理下,memcpy 与memmove区别在于数据拷贝时是否能够处理内存重叠的问题,也就是踩地址的问题。对于正常使用new或者malloc来申请新内存空间肯定不会返回一个与已有内存重叠的地址,除非系统内存池损坏了(这也是我最初感觉memmove没意义的原因)。它的应用场景主要有以下:

使用场景

  • 数组元素或字符串中的部分移动(与move对应上了有没有~)
  • 结构体数组中数据项的移动
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    // 删除字符串中的第 3 个字符(从 'l' 开始)
    memmove(str + 2, str + 3, strlen(str) - 2);
    // 输出修改后的字符串,Helo, World!
    printf("%s\n", str);
    
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    // 假设我们要将数组中从索引 3 开始的 4 个元素向前移动
    memmove(arr, arr + 3, 4 * sizeof(int));
     // 输出数组内容:4 5 6 7 5 6 7 8 9 10
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    
    return 0;
}

memcpy 与memmove内部实现

同样是简单原理上的实现,glibc源码中会增加有内存对齐处理、奇偶长度分别处理等优化方式

memcpy

如下所示,memcpy逐字节拷贝,如果是安全函数,则会在拷贝之前判断源地址长度是否大于目的地址长度,来保证目的地址内存不会溢出。

void* mymemcpy(void* dest, void* sorc, size_t len)
{
    unsigned char* dest_c = (unsigned char*) dest;
    unsigned char* sorc_c = (unsigned char*) sorc;
    while (len > 0)
    {
        *dest_c = *sorc_c;
        dest_c++;
        sorc_c++;
        len--;
    }
    return dest;
}

memmove

memmove会增加考虑源地址与目的地址是否重叠的情况,如果两者重叠,存在两种情况:

  • 源地址在前,目标地址在后:那么如果按照memcpy那样从前向后逐字节拷贝,就会存在元数据被覆盖,所以需要修改为从后向前拷贝;
  • 目标地址在前,源地址在后:按照memcpy那样从前向后逐字节拷贝没有问题。

那么该如何判断源地址与目标地址是否重叠呢?这前提要清楚源地址和目的地址都是一段连续的长度为len的内存空间(不关系目的地址空间是否足够),所以只要两块内存首地址只差大于len,那么就不存在重叠(不用挨个比较源和目的地址各个字节地址是否相同。。。),然后根据差值的正负关系判断源地址与目的地址哪个在前。

void* mymemmove(void* dest, void* sorc, size_t len)
{
    int dis = (int*)dest - (int*)sorc;
    unsigned char* dest_c = (unsigned char*)dest;
    unsigned char* sorc_c = (unsigned char*)sorc;

    if (dis > 0 && dis < len)//目的地址大于源地址,源地址在前,目的地址在后
    {
        for (size_t i = len-1; i >0 ; i--)
        {
            dest_c[i] = sorc_c[i];
        }
        dest_c[0] = sorc_c[0];
    }
    else //不重叠,或者目的地址在前,源地址在后
    {
        for (size_t i = 0; i < len; i++)
        {
            dest_c[i] = sorc_c[i];
        }
    }

    return dest;
}

需要注意的是,在使用memove后可能导致源地址数据被错误修改,所以如果不是目的明确,不建议继续使用源地址指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值