Assembly 将递归性转换为汇编 我试图在汇编中翻译一个C++递归代码。我在32位模式下工作 这是C++代码。< /P> 1 extern "C" void output (unsigned); 2 extern "C" void parcours (unsigned[], unsigned, unsigned = 0); 3 4 void parcours (unsigned v[], unsigned n, unsigned k) { 5 while (k < n) { 6 output(v[k]); 7 parcours(v, n, 2*k+1); 8 k = 2*k+2; 9 } 10 } 1外部“C”无效输出(无符号); 2个外部“C”无效地块(无符号[],无符号,无符号=0); 3. 4个无效地块(无符号v[],无符号n,无符号k){ 5时(k这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途 您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是-S(-fverbose asm也很有用)。对于Microsoft编译器,此选项为

Assembly 将递归性转换为汇编 我试图在汇编中翻译一个C++递归代码。我在32位模式下工作 这是C++代码。< /P> 1 extern "C" void output (unsigned); 2 extern "C" void parcours (unsigned[], unsigned, unsigned = 0); 3 4 void parcours (unsigned v[], unsigned n, unsigned k) { 5 while (k < n) { 6 output(v[k]); 7 parcours(v, n, 2*k+1); 8 k = 2*k+2; 9 } 10 } 1外部“C”无效输出(无符号); 2个外部“C”无效地块(无符号[],无符号,无符号=0); 3. 4个无效地块(无符号v[],无符号n,无符号k){ 5时(k这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途 您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是-S(-fverbose asm也很有用)。对于Microsoft编译器,此选项为,assembly,x86,nasm,Assembly,X86,Nasm,下面是我翻译它的尝试。我正在努力将递归C++(或任何其他高级语言)翻译成程序集。如果您能更正此代码并指出我的错误,我将不胜感激 %include "io.inc" section .text global start start: mov edi, [ebp+8] ;address of the first element of the array mov eax, [ebp+12] ;n mov edx, [ebp+16] ;k parcou

下面是我翻译它的尝试。我正在努力将递归C++(或任何其他高级语言)翻译成程序集。如果您能更正此代码并指出我的错误,我将不胜感激

%include "io.inc"

section .text
global start

start:    
    mov edi, [ebp+8]    ;address of the first element of the array
    mov eax, [ebp+12]   ;n
    mov edx, [ebp+16]   ;k

parcours:
    enter 0,0           ;stack frame creation
    push edx            ;we save k before the modification

    shl edx,1
    add edx,1 ;2*k+1


loop:
    cmp edx,eax
    jb endloop          ;if k < n

    push [edi+edx]      ;we push for the output
    call output
    pop ebx

    call parcours
    shl edx,1           ;2*k
    add edx,2           ;2*k+2
    jmp loop

endloop:
    pop edx
    leave               ;stack frame destruction
    ret
%包括“io.inc”
第节.案文
全球启动
开始:
移动电子数据交换[ebp+8];数组的第一个元素的地址
mov-eax,[ebp+12];N
移动edx,[ebp+16];K
地块:
输入0,0;堆栈帧创建
推动edx;我们在修改前保存了k
shl edx,1
加edx,1;2*k+1
循环:
cmp-edx,eax
jb端环;如果k > P>这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途

您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是

-S
-fverbose asm
也很有用)。对于Microsoft编译器,此选项为

或者,您可以编译代码,然后使用类似于
objdump
(Gnu)或
dumpbin
(MS)的工具来显示目标代码的程序集列表。(如果你用调试符号编译,这有很好的效果,在汇编指令中交织相应的C++语句,至少是编译器的能力最好的,因为不一定有1到1的映射,更不用说为了优化目的而指令重新排序的问题)。 两者都有相同的效果。你可以这样做:

 extern "C" void output (unsigned);
 extern "C" void parcours (unsigned[], unsigned, unsigned = 0);

 void parcours (unsigned v[], unsigned n, unsigned k) {
     while (k < n) {
         output(v[k]);
         parcours(v, n, 2*k+1);
         k = 2*k+2;
     }
 }
这是优化编译器为
parcours
函数生成的汇编语言代码。从这里开始,您需要做两件事:

  • 仔细阅读代码,确保您理解每条指令的作用、编译器发出指令的原因以及函数作为一个整体是如何工作的。像计算机一样跟踪代码,确保理解代码

  • 在其周围编写一个包装器,其中包含汇编程序通常需要的内容,如
    section.text
    和入口点

  • 请注意,递归只需从
    parcours
    函数中再次调用
    parcours
    函数即可实现。C++和汇编的唯一区别在于C++使用了代码> PARCORs(v,n,2 *k+ 1);<代码>,而程序集使用一系列
    推送
    指令(用于参数),然后是
    调用包

    与您编写的代码相比,此代码有什么不同之处

    首先,正如评论中所讨论的,它遵循一种标准的调用约定,其中非易失性寄存器保留在顶部(使用一系列
    push
    指令),并在底部恢复(使用一系列相应的
    pop
    指令)。参数在堆栈上以标准方式从右向左传递

    此外,优化编译器(毫不奇怪)比您更擅长生成最佳代码序列。特别是,他们知道使用
    enter
    leave
    创建/销毁堆栈帧的速度很慢,因此他们直接操作堆栈指针(
    esp
    ),并且仅在需要时/在需要时才这样做。另一个例子是,他们知道
    shl reg,1
    相当于
    add reg,reg
    ,但后者速度更快。他们知道一些可爱的小技巧,比如
    leaeax,[ebx+1]
    是一种有效的方法,可以在
    ebx
    的值上加1,同时将结果存储在
    eax
    中,而无需修改
    ebx

    不过,最重要的是,它们有簿记算法,可以在执行递归函数调用时保持堆栈的正确平衡,这是您在尝试时无法正确执行的操作,这就是导致分段错误的原因


    <>我将让你做一个详细的逐行比较C++编译器生成的尝试。这是学习如何编写汇编代码的一种非常有效的方法。就像使用调试器逐步检查代码一样,可以准确地看到代码偏离轨道的地方。现在尝试这样做还不算太晚,并将结果与这个正确的代码进行比较。

    < P>,有这样的工具,专门用于将C++代码转换成程序集的目的。这类工具被称为“编译器”,这正是它们的主要用途

    您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是

    -S
    -fverbose asm
    也很有用)。对于Microsoft编译器,此选项为

    或者,您可以编译代码,然后使用类似于
    objdump
    (Gnu)或
    dumpbin
    (MS)的工具来显示目标代码的程序集列表。(如果您使用调试符号编译,那么在汇编指令中,将相应的C++语句交织在一起,至少对编译器的AB最好,这是很好的效果。
    parcours:
            push    edi
            push    esi
            push    ebx
            mov     esi, DWORD PTR [esp+20]
            mov     ebx, DWORD PTR [esp+24]
            mov     edi, DWORD PTR [esp+16]
            cmp     ebx, esi
            jnb     .L1
    .L3:
            sub     esp, 12
            push    DWORD PTR [edi+ebx*4]
            add     ebx, ebx
            call    output
            lea     eax, [ebx+1]
            add     esp, 12
            add     ebx, 2
            push    eax
            push    esi
            push    edi
            call    parcours
            add     esp, 16
            cmp     esi, ebx
            ja      .L3
    .L1:
            pop     ebx
            pop     esi
            pop     edi
            ret