memcpy正确的实施方法

时间:2013-11-12 15:50:59

标签: c algorithm buffer memcpy

我找到了memcpy的以下实现(面试问题,其中迭代次数〜size / 4):

void memcpy(void* dest, void* src, int size)
{
    uint8_t *pdest = (uint8_t*) dest;
    uint8_t *psrc = (uint8_t*) src;

    int loops = (size / sizeof(uint32_t));
    for(int index = 0; index < loops; ++index)
    {
        *((uint32_t*)pdest) = *((uint32_t*)psrc);
        pdest += sizeof(uint32_t);
        psrc += sizeof(uint32_t);
    }

    loops = (size % sizeof(uint32_t));
    for (int index = 0; index < loops; ++index)
    {
        *pdest = *psrc;
        ++pdest;
        ++psrc;
    }
}

而且我不确定我理解它.......

1)为什么定义uint8_t *pdest,uint8_t *psrc并在此之后进行投射uint32_t -

*((uint32_t*)pdest) = *((uint32_t*)psrc);

我认为从一开始pdestpsrc应该定义为uint32_t ...我缺少什么? 2)我认为这个实现存在问题: 如果src = 0x100dst = 0x104 并且src(最初)看起来像那样:

-------------------------
|  6  |  8  |  7  |  1  |
-------------------------    
0x100  0x104 0x108 0x1C0

执行后就像那样

-------------------------
|  6  |  6  |  6  |  6  |.....
-------------------------
0x100  0x104 0x108 0x1C0

尽管看起来以下内存布局应该是结果

-------------------------
|  6  |  6  |  8  |  7  |....
-------------------------
0x100  0x104 0x108 0x1C0

3 个答案:

答案 0 :(得分:7)

这个memcpy()遇到了另一个问题:如果一个或两个缓冲区不在适当的边界上会发生什么?这可能会显着影响性能,或者在某些体系结构上,甚至无法运行代码。另一个常见问题(但不是这里)是处理缓冲区,其长度不是native(uint32)类型宽度的倍数。您的示例使用uint8类型(然后根据需要进行转换)的原因是允许复制尾随字节而不进行强制转换。如果你转换大部分传输或只是尾随字节,它没有任何区别。考虑到缓冲区对齐,您可能会提前复制初始未对齐数据,直到建立对齐为止。

当源和目标重叠时,memcpy()函数不能保证以定义的方式工作;因此,您标记为第二个问题不是问题。如果代替memcpy(),此代码用于memmove()的实现,则问题将是真实的。

答案 1 :(得分:0)

关于指针类型:这里的想法是,为了减少循环和复制开销,您希望使用最大的数据“块”(比如32位)进行复制。 因此,您尝试使用32位字进行尽可能多的复制。 然后需要将剩余部分复制到较小的8位“块”中。 例如,如果要复制13个字节,则需要复制32位字的3次迭代+复制单个字节的1次迭代。这比单字节复制的13次迭代更可取。 你可以转换为uint32_t *,但是你必须转换回uint8_t *才能完成余下的工作。

关于第二个问题 - 如果目标地址与源缓冲区重叠,则此实现将无法正常工作。假设你也想支持这种memcpy - 这是一个bug。 这是一个受欢迎的采访问题陷阱;)。

答案 2 :(得分:0)

第一个循环通过并将内存从psrc复制到pdest,每个循环包含4个字节的块,因此转换为uint32_t *。第二个循环以1个字节的块为单位复制剩余的内存。对于大块内存,这有效地将迭代次数减少了4倍。

关于为什么演员是uint8_t *而不是uint32_t *的原因。通过直接转换为uint32_t *,第一个循环可以正常工作,但是你需要将指针递增1而不是每个循环4。你会得到类似下面的内容

for(int index = 0; index < loops; ++index)
{
    *(pdest) = *(psrc); //no need for cast
    pdest++;            //increment by 1 not 4
    psrc++;
}

然而,对于第二个循环,您需要转换为uint8_t *,并将指针递增1/4。使用指针算法无法做到这一点,所以不可能这样做。

另一种思考方式: loops1:原始内存块中的4个字节块的数量 loops2:剩余的字节数