编译器如何在堆栈上使用默认值为零来初始化本地数组?
How does the compiler initialize local arrays with a default value of zero on the stack?
假设我在我的函数中定义了一个默认值为 0 的 int
局部数组:
void test() {
int array[256] = {0};
}
我的理解是:
数组将存储在堆栈中,方法是将 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 24, %esp
.cfi_offset 7, -12
.cfi_offset 3, -16
leal -1032(%ebp), %ebx
movl [=11=], %eax
movl 6, %edx
movl %ebx, %edi
movl %edx, %ecx
rep stosl
addl 24, %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
我意识到这可能是一个愚蠢的问题,而且我知道每个编译器的行为可能不同,但我想知道具有 256 个零的数组的分配发生在何处。我的假设是正确的还是情况有所不同?
(我已经很长时间没有写汇编了,我在理解发生了什么方面遇到了一些困难)
正在此处进行分配:
subl 24, %esp
它是堆栈指针esp
上的sub
,因为堆栈向下增长。
此处清空数组:
movl [=11=], %eax
movl 6, %edx
movl %ebx, %edi
movl %edx, %ecx
rep stosl
它的作用是:
rep
: 重复字符串操作ecx
次
stosl
:将eax
存入edi
指向的内存中,edi加4,或减4,取决于方向标志。如果很清楚 (cld
),edi
会递增,否则会递减。
请注意,ebx
在代码中稍早一点设置为指向数组的开头。
最后,数组在这里被释放:
addl 24, %esp
这些是亮点,但还有一些注意事项,所以这里是(未优化的)代码的完整列表:
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 24, %esp # allocate 1kb on the stack
leal -1032(%ebp), %ebx # esp + 1024 + 4 + 4 = ebp; equivalent to mov %esp, %ebx
movl [=13=], %eax # the {0}
movl 6, %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 , %edi' ecx times
addl 24, %esp # release the array
popl %ebx # and the preserved registers
popl %edi
popl %ebp
ret
假设我在我的函数中定义了一个默认值为 0 的 int
局部数组:
void test() {
int array[256] = {0};
}
我的理解是:
数组将存储在堆栈中,方法是将 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 24, %esp
.cfi_offset 7, -12
.cfi_offset 3, -16
leal -1032(%ebp), %ebx
movl [=11=], %eax
movl 6, %edx
movl %ebx, %edi
movl %edx, %ecx
rep stosl
addl 24, %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
我意识到这可能是一个愚蠢的问题,而且我知道每个编译器的行为可能不同,但我想知道具有 256 个零的数组的分配发生在何处。我的假设是正确的还是情况有所不同?
(我已经很长时间没有写汇编了,我在理解发生了什么方面遇到了一些困难)
正在此处进行分配:
subl 24, %esp
它是堆栈指针esp
上的sub
,因为堆栈向下增长。
此处清空数组:
movl [=11=], %eax
movl 6, %edx
movl %ebx, %edi
movl %edx, %ecx
rep stosl
它的作用是:
rep
: 重复字符串操作ecx
次stosl
:将eax
存入edi
指向的内存中,edi加4,或减4,取决于方向标志。如果很清楚 (cld
),edi
会递增,否则会递减。 请注意,ebx
在代码中稍早一点设置为指向数组的开头。
最后,数组在这里被释放:
addl 24, %esp
这些是亮点,但还有一些注意事项,所以这里是(未优化的)代码的完整列表:
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 24, %esp # allocate 1kb on the stack
leal -1032(%ebp), %ebx # esp + 1024 + 4 + 4 = ebp; equivalent to mov %esp, %ebx
movl [=13=], %eax # the {0}
movl 6, %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 , %edi' ecx times
addl 24, %esp # release the array
popl %ebx # and the preserved registers
popl %edi
popl %ebp
ret