编译器如何在堆栈上初始化默认值为零的本地数组?

Dom*_*ice 3 c arrays assembly stack-memory

让我们假设我int在函数中定义了一个默认值为0 的本地s 数组:

void test() {
    int array[256] = {0};
}
Run Code Online (Sandbox Code Playgroud)

我对此的理解是:

通过将256个零推入堆栈并因此增加堆栈指针,数组将存储在堆栈中.如果数组没有默认值,那么增加堆栈指针就足够了.

现在这是前一个片段生成的汇编代码:

test:
.LFB2:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %edi
    pushl   %ebx
    subl    $1024, %esp
    .cfi_offset 7, -12
    .cfi_offset 3, -16
    leal    -1032(%ebp), %ebx
    movl    $0, %eax
    movl    $256, %edx
    movl    %ebx, %edi
    movl    %edx, %ecx
    rep stosl
    addl    $1024, %esp
    popl    %ebx
    .cfi_restore 3
    popl    %edi
    .cfi_restore 7
    popl    %ebp
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE2:
    .size   test, .-test
Run Code Online (Sandbox Code Playgroud)

我意识到这可能是一个愚蠢的问题,我知道每个编译器可能采取不同的行为,但我想知道在哪里发生了256个零的数组分配.我的假设是正确的还是这种情况发生了不同?

(我已经很长时间没有写集会了,我在理解正在发生的事情上遇到了一些困难)

Ken*_*ney 8

分配发生在这里:

    subl    $1024, %esp                 
Run Code Online (Sandbox Code Playgroud)

它是sub堆栈指针esp,因为堆栈增长.

数组在这里清除:

    movl    $0, %eax
    movl    $256, %edx     
    movl    %ebx, %edi 
    movl    %edx, %ecx
    rep stosl
Run Code Online (Sandbox Code Playgroud)

这样做是:

  • rep:重复字符串操作ecx次数
  • stosl:存储eax在指向的内存中,edi并向edi添加4,或者减去4,具体取决于方向标志.如果它是clear(cld),则edi递增,否则递减.注意,ebx设置为在代码中稍早一点指向数组的开头.

最后,这里发布了数组:

    addl    $1024, %esp
Run Code Online (Sandbox Code Playgroud)


这些是重点,但还有一些注意事项,所以这里是(非优化的)代码的完整列表:

pushl   %ebp                # preserve caller's ebp (decrements esp by 4)
movl    %esp, %ebp          # copy stack pointer to ebp
pushl   %edi                # preserve for caller
pushl   %ebx                # preserve for caller
subl    $1024, %esp         # allocate 1kb on the stack
leal    -1032(%ebp), %ebx   # esp + 1024 + 4 + 4 = ebp; equivalent to mov %esp, %ebx
movl    $0, %eax            # the {0}
movl    $256, %edx          # the repeat count - could have been stored in ecx directly
movl    %ebx, %edi          # init edi to the start of the array
movl    %edx, %ecx          # put 256 in ecx
rep stosl                   # repeat 'mov %eax, %(edi); add $4, %edi' ecx times
addl    $1024, %esp         # release the array
popl    %ebx                # and the preserved registers
popl    %edi
popl    %ebp                
ret
Run Code Online (Sandbox Code Playgroud)

  • 由于@chqrlie指出方向标志要正确设置,但是大多数调用约定默认方向标志为向前移动清除(CLD).一般规则是,如果你编写一个函数来执行_STD_以反转方向,那么它应该在函数返回之前设置回清除(使用_CLD_).在这种情况下,您当然需要向前移动,因此向前移动的默认值已经正确. (3认同)