代码之家  ›  专栏  ›  技术社区  ›  Angus

了解memcpy()的实现

  •  13
  • Angus  · 技术社区  · 11 年前

    我在查看memcpy.c的实现,发现了一个不同的memcpy代码。我不明白他们为什么要做((ADDRESS)s)|((ADDESS)d)|c)&(大小为(UINT)-1)

    #if !defined(__MACHDEP_MEMFUNC)
    
    #ifdef _MSC_VER
    #pragma function(memcpy)
    #undef __MEMFUNC_ARE_INLINED
    #endif
    
    #if !defined(__MEMFUNC_ARE_INLINED)
    /* Copy C bytes from S to D.
     * Only works if non-overlapping, or if D < S.
     */
    EXTERN_C void * __cdecl memcpy(void *d, const void *s, size_t c)
    {
        if ((((ADDRESS) s) | ((ADDRESS) d) | c) & (sizeof(UINT) - 1)) {
    
            BYTE *pS = (BYTE *) s;
            BYTE *pD = (BYTE *) d;
            BYTE *pE = (BYTE *) (((ADDRESS) s) + c);
    
            while (pS != pE)
                *(pD++) = *(pS++);
        }
        else {
            UINT *pS = (UINT *) s;
            UINT *pD = (UINT *) d;
            UINT *pE = (UINT *) (BYTE *) (((ADDRESS) s) + c);
    
            while (pS != pE)
                *(pD++) = *(pS++);
        }
        return d;
    }
    
    #endif /* ! __MEMFUNC_ARE_INLINED */
    #endif /* ! __MACHDEP_MEMFUNC */
    
    2 回复  |  直到 11 年前
        1
  •  16
  •   Eric Postpischil    2 年前

    该代码正在测试地址是否针对 UINT 。如果是,代码将使用 UINT公司 物体。如果没有,代码将使用 BYTE 物体。

    测试的工作原理是首先对两个地址执行逐位“或”运算。任何一个地址中的任何一个位都将在结果中为on。然后测试执行与 sizeof(UINT) - 1 。预计大小为 UINT公司 是二的幂。那么大小减1将所有低位都打开。例如,如果大小是4或8,那么比它小1,二进制11 2. 或111 2. 。如果其中一个地址不是 UINT公司 ,那么它将打开其中一个位,测试将指示它。(通常,整数对象的最佳对齐方式与其大小相同。这不一定是真的。此代码的现代实现应该使用 _Alignof(UINT) - 1 而不是尺寸。)

    复制时使用 UINT公司 对象的速度更快,因为在硬件级别,一个加载或存储指令加载或存储 UINT公司 (可能是四个字节)。使用这些指令时,处理器的复制速度通常比使用四倍于单字节加载或存储指令时更快。

    当然,此代码依赖于实现;它需要C实现的支持,而C实现不是基本C标准的一部分,并且它取决于执行它的处理器的特定功能。

    更高级的 memcpy 实现可能包含其他功能,例如:

    • 如果其中一个地址已对齐,但另一个未对齐,请使用特殊的加载未对齐指令从一个地址加载多个字节,并将常规存储指令加载到另一个地址。
    • 如果处理器有单指令多数据指令,请使用这些指令在一条指令中加载或存储许多字节(通常为16个,可能更多)。
        2
  •  14
  •   Dietrich Epp    11 年前

    代码

    ((((ADDRESS) s) | ((ADDRESS) d) | c) & (sizeof(UINT) - 1))
    

    检查是否有 s , d c 未与大小对齐 UINT .

    例如,如果 s = 0x7ff30b14 , d = 0x7ffa81d8 ,c= 256 sizeof(UINT) == 4 ,则:

    s         = 0b1111111111100110000101100010100
    d         = 0b1111111111110101000000111011000
    c         = 0b0000000000000000000000100000000
    s | d | c = 0b1111111111110111000101111011100
    (s | d | c) & 3 =                        0b00
    

    所以两个指针都对齐了。在以下指针之间复制内存更容易 二者都 对齐,这只需要一个分支。

    在许多体系结构上, *(UINT *) ptr 如果 ptr 与的宽度正确对齐 UINT公司 在一些架构上, *(UINT*)ptr 如果 ptr程序 未正确对齐。

    推荐文章