x86机器代码(32位保护模式),36字节
52
8B 12
8D 44 91 FC
8B F9
8D 71 04
3B F0
77 10
A7
75 F9
83 EF 04
4A
4A
A5
3B F8
75 FB
97
EB E7
58
89 10
C3
上面的机器代码字节定义了一个函数,该函数将数组作为输入,就地折叠相邻的重复项,并返回到调用方而不返回结果。它遵循__fastcall
调用约定,分别在ECX
和EDX
寄存器中传递两个参数。
第一个参数(ECX
)是指向32位整数数组中第一个元素的指针(如果数组为空,则可以指向内存中的任何位置)。第二个参数(EDX
)是指向包含数组长度的32位整数的指针。
如果需要,该函数将就地修改数组的元素,并更新长度以指示折叠数组的新长度。这是获取输入并返回输出的一种不寻常的方法,但是在汇编语言中您别无选择。与C中一样,数组实际上用语言表示为指向第一个元素的指针和一个length。唯一有点奇怪的是通过引用获取长度,但是如果我们不这样做,将无法缩短数组。该代码可以正常工作,但是输出将包含垃圾,因为调用者不知道在哪里停止从折叠数组中打印元素。
非高尔夫装配助记符:
; void __fastcall CollapseAdjacentDuplicates(int * ptrArray, int * ptrLength);
; ECX = ptrArray ; ECX = fixed ptr to first element
; EDX = ptrLength
push edx ; save pointer to the length
mov edx, [edx] ; EDX = actual length of the array
lea eax, [ecx+edx*4-4] ; EAX = fixed ptr to last element
FindAdjacentPairs:
mov edi, ecx ; EDI = ptr to element A
lea esi, [ecx+4] ; ESI = ptr to element B
FindNext:
cmp esi, eax ; is ptr to element B at end?
ja Finished ; if we've reached the end, we're finished
cmpsd ; compare DWORDs at ESI and EDI, set flags, and increment both by 4
jne FindNext ; keep looping if this is not a pair
; Found an adjacent pair, so remove it from the array.
sub edi, 4 ; undo increment of EDI so it points at element A
dec edx ; decrease length of the array by 2
dec edx ; (two 1-byte DECs are shorter than one 3-byte SUB)
RemoveAdjacentPair:
movsd ; move DWORD at ESI to EDI, and increment both by 4
cmp edi, eax ; have we reached the end?
jne RemoveAdjacentPair ; keep going until we've reached the end
xchg eax, edi ; set new end by updating fixed ptr to last element
jmp FindAdjacentPairs ; restart search for adjacent pairs from beginning
Finished:
pop eax ; retrieve pointer to the length
mov [eax], edx ; update length for caller
ret
该实现受到我C ++ 11答案的启发,但在汇编中进行了精心重写,以实现尺寸的优化。汇编是一种更好的高尔夫语言。:-)
注意:由于此代码使用字符串指令,因此确实假定方向标志是清除的(DF
== 0)。在大多数操作环境中,这是一个合理的假设,因为ABI通常要求DF必须清晰。如果不能保证,则需要在代码顶部插入一个1字节的CLD
指令(0xFC
)。
如前所述,它也假定为32位保护模式,特别是“平面”存储模型,其中额外的段(ES
)与数据段(DS
)相同。