11

我正在寻找 memcpy.c 的实现,我发现了一个不同的 memcpy 代码。我不明白他们为什么这样做 (((ADDRESS) 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 */
4

2 回答 2

14

该代码正在测试地址是否与UINT. UINT如果是这样,则代码使用对象进行复制。BYTE如果不是,则代码使用对象进行复制。

该测试首先对两个地址进行按位或运算。在任一地址中打开的任何位都将在结果中打开。然后测试执行按位与sizeof(UINT) - 1。预计 a 的大小UINT是 2 的某个幂。然后大小减一具有所有低位。例如,如果大小是 4 或 8,则比这小一,即二进制 11 2或 111 2。如果任一地址不是 a 大小的倍数UINT,那么它将打开这些位中的一个,并且测试将指示它。(通常,整数对象的最佳对齐方式与其大小相同。这不一定是正确的。此代码的现代实现应该使用_Alignof(UINT) - 1而不是大小。)

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

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

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

  • 如果其中一个地址对齐但另一个未对齐,则使用特殊的加载未对齐指令从一个地址加载多个字节,并使用常规存储指令到另一个地址。
  • 如果处理器具有单指令多数据指令,请使用这些指令在单个指令中加载或存储多个字节(通常为 16 个,可能更多)。
于 2013-10-04T17:47:51.630 回答
13

编码

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

检查s,dc是否与 a 的大小不对齐UINT

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

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

所以两个指针都是对齐的。在两个对齐的指针之间复制内存更容易,并且只需一个分支即可。

在许多架构上,如果正确对齐到 a 的宽度*(UINT *) ptr快得多。在某些架构上,如果没有正确对齐,实际上会崩溃。ptrUINT*(UINT *) ptrptr

于 2013-10-04T17:47:24.940 回答