问题描述
我试图将 memmove 从 C 实现到 x86 程序集,所以我写道:
start:
movb source(%eax),%cl
movb %cl,destination(%eax)
inc %eax
cmp num,%eax
jne start
end:
但这是错误的,为什么呢?根据:http://www.cplusplus.com/reference/cstring/memmove/
将 num 个字节的值从 source 指向的位置复制到 目标指向的内存块。 复制就好像 使用了中间缓冲区,允许目的地和 来源重叠。
如何在不使用堆栈的情况下解决此问题? 注意:我们可以假设源目标刚进入内存并且num(要复制的字节数)很远并且不会被错误触及。
解决方法
这里的问题是关于 destination - source < size
情况下的潜在重叠(即 source
和 destination
指向同一块数据)。发生这种情况时,您的情况如下:
AAAAAAAAAAAAAABBBBBBBBBBBBBB_______________________
^ ^ ^ ^
source destination source destination
+ num + num
如果您从 source
开始复制,您将覆盖您要复制的部分内容(在本例中,您将用 B
覆盖 A
),丢失原始值,最后是这样的:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_________
^ ^ ^ ^
source destination source destination
+ num + num
实际上你想要这个时:
AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBB_________
^ ^ ^ ^
source destination source destination
+ num + num
您可以通过检查何时 destination - source < num
来解决此问题,并在这种情况下反向复制(以 eax = num
开头)。
相应的程序集应该是这样的:
mov $destination,%eax
sub $source,%eax # dst-src distance in bytes
cmp num,%eax
jb backwards # if (dst-src < copy_size) goto backwards
forward:
mov $0,%eax
forward_loop:
movb source(%eax),%cl
movb %cl,destination(%eax)
inc %eax
cmp num,%eax
jne forward_loop
jmp end
backwards:
movl num,%eax # start from i=length
backwards_loop:
movb source-1(%eax),%cl
movb %cl,destination-1(%eax)
dec %eax
jnz backwards_loop
end: