假设您必须同时实现两者,则实现可能如下所示:
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src) {
// Copy from front to back
}
}
void memcpy ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src != (uintptr_t)dst) {
// Copy in any way you want
}
}
这应该很好地解释了差异。memmove总是以这样的方式复制,如果src和dst重叠它仍然是安全的,而memcpy正如文档所说的那样,使用时并不关心memcpy,两个内存区域不能重叠。
例如,如果memcpy复制“从前到后”并且内存块是这样对齐的
[---- src ----]
[---- dst ---]
复制srcto的第一个字节dst已经破坏了src复制之前最后一个字节的内容。只有“从后到前”复制才能得到正确的结果。
现在交换src和dst:
[---- dst ----]
[---- src ---]
在这种情况下,复制“从前到后”是安全的,因为src在复制第一个字节时,复制“从后到前”会破坏它的前面。
您可能已经注意到,memmove上面的实现甚至没有测试它们是否真的重叠,它只是检查它们的相对位置,但仅此一点就可以使副本安全。由于memcpy通常使用最快的方式在任何系统上复制内存,memmove通常是这样实现的:
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst
&& (uintptr_t)src + count > (uintptr_t)dst
) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src
&& (uintptr_t)dst + count > (uintptr_t)src
) {
// Copy from front to back
} else {
// They don't overlap for sure
memcpy(dst, src, count);
}
}
有时,如果memcpy总是“从前到后”或“从后到前”复制,memmove也可以memcpy在其中一种重叠情况下使用,但memcpy甚至可以根据数据的对齐方式和/或数据量以不同的方式复制复制,因此即使您测试了memcpy系统上的复制方式,您也不能依赖该测试结果始终正确。
在决定打电话给哪一个时,这对您意味着什么?
除非您确定src并且dst不重叠,否则调用memmove,因为它总是会导致正确的结果,并且对于您需要的复制案例通常尽可能快。
如果您确定src并且dst不重叠,请调用memcpy,因为无论您调用哪个结果都无关紧要,在这种情况下两者都可以正常工作,但memmove永远不会比你更快memcpy,如果你不走运,它甚至可能慢一点,所以你只能赢得 call memcpy。