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

标签 linux pointers assembly x86 att

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

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

.section .data
data_items:             #These are the data items
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

.section .text
.globl _start
_start:
    movl $0, %edi                   # move 0 into the index register
    movl data_items(,%edi,4), %eax  # load the first byte of data
    movl %eax, %ebx                 # since this is the first item, %eax is
                                    # the biggest
start_loop:                     # start loop
    cmpl $0, %eax                   # check to see if we’ve hit the end
    je loop_exit
    incl %edi                       # load next value
    movl data_items(,%edi,4), %eax
    cmpl %ebx, %eax                 # compare values
    jle start_loop                  # jump to loop beginning if the new
                                    # one isn’t bigger
    movl %eax, %ebx                 # move the value as the largest
    jmp start_loop                  # jump to loop beginning
loop_exit:
    # %ebx is the status code for the exit system call
    # and it already has the maximum number
    movl $1, %eax   #1 is the exit() syscall
    int $0x80

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

最佳答案

您只需使用 mov 即可做到这一点和cmp ,否 lea需要计算结束指针。 (无论如何,您都没有可以与 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

您不需要在寄存器中使用该地址;您可以使用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
  ...

更高效的是 do{}while loop structure like compilers use ,特别是因为您知道数组包含超过 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)

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


循环和指针的其他示例:

关于linux - 如何使用静态数组的结束指针作为循环条件来比较 x86 中的地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68463932/

相关文章:

linux - linux内核中的调度算法

c++ - 将指针传递给函数只会改变函数作用域中的值

assembly - 设置视频模式或清屏后文本模式光标消失

c - 指向 C 函数的指针

assembly - 如何在 EDX 中打印乘法结果 :EAX in Assembly

assembly - 如何在Linux上从c源代码生成nasm可编译的汇编代码?

c - 后台进程与前台进程

linux - 更改 JENKINS_HOME 时出现 "Unable to create home directory"错误

c++ - 如何知道一个进程是否已经启动但在 Linux 中崩溃了

c++ - 我的指针导致 "same type qualifier used more than once"警告?