如果目标和源相同,是否memmove
仍然“移动”数据(或直接返回)?怎么样realloc
; 如果新尺寸与旧尺寸相同怎么办?
4 回答
这实际上将是特定于实现的。当然,这样做是个好习惯,但这实际上取决于您的意思是哪种实现。
无论哪种方式,它都会起作用,但大概一个适当聪明的实现会检查重叠段(特别是对于 where 的情况source == dest
)并适当地处理它。
据我所知,没有标准给出任何关于在这种情况下立即返回的承诺,所以你不应该期待这种行为。
最好不要传递无效指针,希望它不会访问数据;-)
至少对于realloc
,隐含地假设“不需要移动”条件存在并且是有效的,因为移动被记录为一种特殊情况:
realloc() 函数应将 ptr 指向的内存对象的大小更改为 size 指定的大小。对象的内容应保持不变,直至新旧尺寸中的较小者。如果内存对象的新大小需要移动对象,则释放对象先前实例化的空间。
“如果……会”的措辞表明这不一定总是如此。当然,实现完全不需要省略不必要的副本。
唯一的要求memmove
是最终效果与先将数据复制到临时缓冲区然后再复制到最终目的地的效果相同。这种“好像”约束允许在不破坏数据的情况下复制重叠区域(我知道没有真正首先复制到临时缓冲区的实现)。
所以,一言以蔽之:未指定。
正如其他人所说,规范不需要快捷方式,并且不清楚添加额外的分支何时能真正提高性能。
但这并不能回答你打电话时实际发生了什么的问题memmove
。我仔细研究了 glibc 源代码,发现了许多适用于各种架构的汇编实现以及可移植的 C 实现。
TL;DR 是纯 C 版本没有快捷方式,但 x86_64 汇编版本有(我认为)。
纯 C 版本是一个相当标准的 memmove 循环。一个技巧是,如果您移动 16 KiB 或更多,它将操作虚拟内存而不是复制字节。该函数在string/memmove.c中定义,实现的核心是BYTE_COPY_FWD
来自sysdeps/generic/memcopy.h的宏。
对于 x86_64 程序集,有几个版本取决于可用的指令(例如 AVX、SSE 等)。这些在sysdeps/x86_64/multiarch中。
这是memmove-vec-unaligned-erms.S 的一种实现,它使用增强型 REP MOVSB (ERMS):
ENTRY (__memmove_erms)
movq %rdi, %rax
/* Skip zero length. */
test %RDX_LP, %RDX_LP
jz 2f
L(start_movsb):
mov %RDX_LP, %RCX_LP
cmp %RSI_LP, %RDI_LP
jb 1f
/* Source == destination is less common. */
je 2f
lea (%rsi,%rcx), %RDX_LP
cmp %RDX_LP, %RDI_LP
jb L(movsb_backward)
1:
rep movsb
2:
ret
L(movsb_backward):
leaq -1(%rdi,%rcx), %rdi
leaq -1(%rsi,%rcx), %rsi
std
rep movsb
cld
ret
END (__memmove_erms)
我阅读汇编的能力不是很好,但据我所知,这个实现确实缩短了源和目标相同的情况。
如果我没看错的话,它会从比较指针开始。如果目标在源之前,它会跳转到标签 1 ( jb 1f
),它调用rep movsb
. 据我了解,这基本上是memcpy
. 如果指针相等,则跳转到je 2f
立即返回的标签 2 ( )。否则,它会安排 arep movsb
在数据上反向运行。
我还查看了sysdeps/x86_64/multiarch/memcpy-ssse3.S中的 SSSE3 实现。该版本似乎也实现了快捷方式。
显然,这一切只适用于 glibc。我还没有检查 llvm-libc。