我刚刚编写了一个整数数组的 bubble_sort(请参阅上一个问题),并决定忽略标准交换并实现程序集交换,如下所示:
int swap(int* x, int* y)
{
if(x != y)
{
_asm
{
mov eax,[x];
mov ebx, [y];
mov [y],eax;
mov [x], ebx;
}
}
return 0;
}
我实际上确信它将按原样插入到生成的代码中并且能够正常工作。 好吧,我使用此交换的代码确实有效,但我研究了编译器将其变成的内容,并且我的交换被更改为:
if(x != y)
00E01A6F inc ebp
00E01A70 or byte ptr [ebx],bh
00E01A72 inc ebp
00E01A73 or al,74h
if(x != y)
00E01A75 or al,8Bh
{
_asm
{
mov eax,[x];
00E01A77 inc ebp
00E01A78 or byte ptr [ebx+45890C5Dh],cl
mov [y],eax;
00E01A7E or al,89h
mov [x], ebx;
00E01A80 pop ebp
00E01A81 or byte ptr [ebx],dh
}
}
return 0;
00E01A83 rcr byte ptr [edi+5Eh],5Bh
}
我已经在 MS VS 2012 中编译了它。 所有这些额外的行是什么意思,为什么它们在那里?为什么我的 _asm 片段不能使用?
你能告诉我们你是如何编译该函数以及如何反汇编的吗?
当我使用
进行编译时cl /FAsc -c test.c
我在内联汇编器部分的汇编列表中得到以下内容:
; 4 : {
; 5 : _asm
0000a 53 push ebx
; 6 : {
; 7 : mov eax,[x];
0000b 8b 44 24 08 mov eax, DWORD PTR _x$[esp]
; 8 : mov ebx, [y];
0000f 8b 5c 24 0c mov ebx, DWORD PTR _y$[esp]
; 9 : mov [y],eax;
00013 89 44 24 0c mov DWORD PTR _y$[esp], eax
; 10 : mov [x], ebx;
00017 89 5c 24 08 mov DWORD PTR _x$[esp], ebx
; 4 : {
; 5 : _asm
0001b 5b pop ebx
$LN4@swap:
; 11 : }
需要注意的一件事是,您没有交换您真正想要交换的内容 - 您交换的是传递给函数的指针,而不是指针引用的项目。 因此,当函数返回时,交换的数据将被丢弃。该功能只是一大亮点。
您可能想尝试以下方法:
_asm
{
mov eax,[x];
mov ebx,[y];
mov ecx, [eax]
mov edx, [ebx]
mov [eax], edx
mov [ebx], ecx
}
但坦率地说,在 C 中执行交换可能会产生类似(或更好)的代码。
缺少第一个和最后一个字节。如果你看看现在的代码是什么:
inc ebp ; 45
or byte ptr [ebx],bh ; 08 3B
inc ebp ; 45
or al,74h ; 0C 74
or al,8Bh ; 0C 8B
inc ebp ; 45
or byte ptr [ebx+45890C5Dh],cl ; 08 8B 5D 0C 89 45
or al,89h ; 0C 89
pop ebp ; 5B
or byte ptr [ebx],dh ; 08 33
rcr byte ptr [edi+5Eh],5Bh ; C0 5F 5E 5B
如果忽略前两个字节,您会得到以下结果:
cmp eax, [ebp + 12] ; 3B 45 0C
jz skip ; 74 0C
mov eax, [ebx + 8] ; 8B 45 08
mov ebx, [ebp + 12] ; 8B 5D 0C
mov [ebp + 12], eax ; 89 45 0C
mov [ebx + 8], ebx ; 89 5B 08
skip:
xor eax, eax ; 33 C0
pop edi ; 5F
pop esi ; 5E
pop ebp ; 5B
它缺少最后的 ret,而且最重要的是,一些以
eax
和 [ebp + 8]
作为参数的指令(mov
在那里是有意义的)。丢失的第一个字节使反汇编与指令流不同步。
当然,它也缺少序言。
如果你想看到 main() 的结尾,你需要在开头压入并在最后弹出:)
_asm
{
push eax //back-up of registers
push ebx
mov eax,[x];
mov ebx, [y];
mov [y],eax;
mov [x], ebx;
pop ebx //resume the registers where they were
pop eax // so, compiler can continue working normally
}
因为编译器也将它们用于其他用途!
您也可以使用
xchg
mov eax,[x]
xchg eax, [y]
mov [x],eax
你是64位的吗?然后是单次读取、单次交换、单次写入。你可以搜索一下。
美好的一天!