如何使用静态数组的结束指针作为循环条件来比较 x86 中的地址?

das*_*20s 2 linux x86 assembly pointers att

从头开始编程的挑战之一是“修改程序以使用结束地址而不是数字 0 来知道何时停止。”

\n

我发现很难做到这一点,因为到目前为止,这本书只介绍了movl,,cmplincl以及寻址模式)和jmp指令。基本上,下面的代码片段中的所有内容都是到目前为止所介绍的。我发现的所有解决方案都涉及本书中尚未介绍的说明。下面的代码查找集合中的最大值。

\n
.section .data\ndata_items:             #These are the data items\n.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0\n\n.section .text\n.globl _start\n_start:\n    movl $0, %edi                   # move 0 into the index register\n    movl data_items(,%edi,4), %eax  # load the first byte of data\n    movl %eax, %ebx                 # since this is the first item, %eax is\n                                    # the biggest\nstart_loop:                     # start loop\n    cmpl $0, %eax                   # check to see if we\xe2\x80\x99ve hit the end\n    je loop_exit\n    incl %edi                       # load next value\n    movl data_items(,%edi,4), %eax\n    cmpl %ebx, %eax                 # compare values\n    jle start_loop                  # jump to loop beginning if the new\n                                    # one isn\xe2\x80\x99t bigger\n    movl %eax, %ebx                 # move the value as the largest\n    jmp start_loop                  # jump to loop beginning\nloop_exit:\n    # %ebx is the status code for the exit system call\n    # and it already has the maximum number\n    movl $1, %eax   #1 is the exit() syscall\n    int $0x80\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这个问题与随后的问题明显不同,后者要求修改程序以使用长度计数而不是数字 0。对我来说,数组中最后一个数字的地址似乎应该存储在寄存器中,然后进行比较到指针的地址。我无法找到一种适合本书进展的方法,因为到目前为止,这本书只介绍了大概的框架。

\n

Pet*_*des 5

您只需使用mov和即可完成此操作cmplea无需计算结束指针。(无论如何,您都没有可以与 LEA 一起使用的长度)。

您应该在数组末尾添加一个新标签,以便可以引用内存中的该位置(也称为地址)。0并从数组中 删除终止符,因为我们使用的是地址而不是哨兵值。

.section .data
data_items:
  .long 3,67,34,222,45,75,54,34,44,33,22,11,66     # ,0   remove the sentinel / terminator
data_items_end:                                  # and add this new label
Run Code Online (Sandbox Code Playgroud)

您不需要在寄存器中保存该地址;您可以将cmp $data_items_end, %reg其用作立即数,链接器将正确的字节填充到机器代码中,就像为您的mov data_items(,%edi,4), %eax. (cmp symbol, %reg将与该地址处的内存进行比较。 $symbol该地址是 AT&T 语法中的立即数。)

您在寄存器中需要的是起始地址,因此您可以递增和取消引用它。(对于采用指针+长度的函数,您可以计算寄存器中的结束地址。)

_start:
    mov  $data_items, %edi       # int *ptr = &data_items[0]
    mov  (%edi), %ebx            # current max
   # setting %eax is unnecessary here, it's always written before being read in this and the original version
loop_start:
    add  $4, %edi                # ptr++  (4 byte elements)
    cmp  $data_items_end, %edi
    je   loop_exit               # if (ptr == endp) break
    ...                  # compare with (%edi) and update %ebx if greater.
    jmp  loop_start
  ...
Run Code Online (Sandbox Code Playgroud)

更有效的是do{}while像编译器使用的循环结构,特别是因为您知道数组包含超过 1 个元素,所以您不需要检查循环体应该运行 0 次的情况。jmp请注意,除了 cmp/jcc 之外,没有每次都必须执行的无条件条件。

_start:
    mov  $data_items, %edi       # int *ptr = &data_items[0]
    mov  (%edi), %ebx            # current max

loop_start:                    # do{
    add  $4, %edi                # ptr++;  (4 byte elements)
  ## maybe update max:
    mov  (%edi), %eax            # tmp = *ptr;
    cmp  %ebx, %eax
    cmovg %eax, %ebx             # max = (tmp > max) ? tmp : max;
  ## end of loop body

    cmp  $data_items_end, %edi
    jne  loop_start            # }while(ptr != endp)
## end of loop, but nothing jumps here so no label is needed.

    mov  $1, %eax
    int  $0x80             # SYS_exit(%ebx)
Run Code Online (Sandbox Code Playgroud)

我使用cmp/ cmovg(条件移动)而不是分支只是因为输入的指令较少并且循环内没有分支,从而更容易查看循环结构。


循环和指针的其他示例: