了解memcpy()的实现

时间:2013-10-04 17:45:29

标签: c memcpy language-implementation

我正在寻找memcpy.c的实现,我发现了一个不同的memcpy代码。我无法理解他们为什么这样做(((地址)s)|((ADDRESS)d)| c)& (sizeof(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 个答案:

答案 0 :(得分:14)

代码正在测试地址是否适合UINT。如果是,则代码使用UINT个对象进行复制。如果没有,代码将使用BYTE个对象进行复制。

测试通过首先执行两个地址的按位OR来工作。任一地址中的任何位都将在结果中打开。然后,测试与sizeof(UINT) - 1执行按位AND。预计UINT的大小是2的幂。然后,大小减去1的所有低位都打开。例如,如果大小为4或8,则小于4,小于11 2 或111 2 。如果任一地址不是UINT大小的倍数,那么它将打开其中一个位,测试将指示它。 (通常,整数对象的最佳对齐方式与其大小相同。这不一定正确。此代码的现代实现应使用_Alignof(UINT) - 1而不是大小。)

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

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

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

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

答案 1 :(得分:13)

代码

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

检查sdc是否与UINT的大小不对齐。

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

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

因此两个指针都是对齐的。在两者对齐的指针之间复制内存更容易,而这仅使用一个分支。

在许多体系结构中,如果*(UINT *) ptrptr的宽度正确对齐,则UINT 更快。在某些体系结构中,如果*(UINT *) ptr未正确对齐,ptr实际上会崩溃。