我正在寻找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 */
答案 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
实施可能包含其他功能,例如:
答案 1 :(得分:13)
代码
((((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
实际上会崩溃。