X86汇编中数组访问的解释

Explanation of array accessing in X86 assembly

我有以下 C 函数:

int sum_arr(int b[], int size){
      int counter = size-1;
      int res = 0;
      while(counter >= 0){
          res = res + b[counter];
         counter = counter - 1;
       }
      return res;
}

我从中生成了以下汇编代码:

gcc -Og -S file.c

产生了以下汇编代码(我只包含了感兴趣的部分):

sum_arr:
.LFB41:
    .cfi_startproc
    subl    , %esi
    movl    [=12=], %eax
    jmp     .L2
.L3:
    movslq  %esi, %rdx
    addl    (%rdi,%rdx,4), %eax
    subl    , %esi
.L2:
    testl   %esi, %esi
    jns     .L3
    rep     ret
    .cfi_endproc

我在使用 .L3 时遇到了一些问题。我的理解是它通过将 int 计数器从 32 位寄存器 %esi 移动到 64 位寄存器 %rdx 开始。然后我不明白下面一行:

addl (%rdi,%rdx,4), %eax

特别是 (%rdi,%rdx,4) 部分,它被添加到 %eax 寄存器中的值。 在最后一行,它将计数器减 1。 有人可以帮我解决这个问题吗?

这种形式更容易理解:

sum_arr:
        sub     esi, 1
        js      .L4
        movsx   rsi, esi
        mov     eax, 0
.L3:
        add     eax, DWORD PTR [rdi+rsi*4]
        sub     rsi, 1
        test    esi, esi
        jns     .L3
        ret
.L4:
        mov     eax, 0
        ret

两点说明: 你的整数很可能溢出所以你应该使用 long long 作为临时 & return 值。也可以缩短

long long sum_arr(const int *b, size_t size){
      long long res = 0;
      while(size--){
          res = res + *b++;
       }
      return res;
}
.L3:
    movslq  %esi, %rdx           /* sign extend counter<%esi> to 64bit %rdx */
    addl    (%rdi,%rdx,4), %eax  /* res<%eax> += b<%rdi>[counter<%rdx>]; */
    subl    , %esi             /* counter<%esi> -= 1              */
.L2:
    testl   %esi, %esi           /* do counter<%esi> & counter<%esi> */
    jns     .L3                  /* if result is no 0, jump to L3  */

基本上addl (%rdi,%rdx,4), %eax就是你访问数组(%rdi)的地方,索引为counter(%rdx),然后将元素的值添加到res(%eax), 4 只是内存访问的计数器 (%rdx) 的乘积,因为 int 数组中的每个地址占用系统内存中的 4 个字节。

这行基本上是说 res += MEMORY[addrssOf(b) + counter*4]

顺便说一句,我相信你想检查 int counter = size-1; 行之前的 size > 0,而且正如 P__J__ 在他的回答中提到的那样,你的 res 可能会溢出您求和的数组中每个元素的类型相同。