【发布时间】:2019-01-22 01:25:34
【问题描述】:
我正在尝试更详细地了解 C/C++ 代码的编译过程及其内存管理。假设如下代码:
#include <iostream>
int main() {
int a = 5;
int *b = (int *) malloc(40);
return 0;
}
我知道a 和b 将在堆栈上创建,b 的值(它指向的内存)将在堆上。
编译成汇编的代码如下所示:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $40, %eax
movl %eax, %edi
movl $0, -4(%rbp)
movl $5, -8(%rbp)
callq _malloc
xorl %ecx, %ecx
movq %rax, -16(%rbp)
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
我的问题是;
a is on the stack (memory) 在这里是什么意思?根据上面的汇编,a 直接嵌入到指令$5, -8(%rbp) 中,没有对内存位置的引用。如果它在内存中,那么a 的地址是什么?
我知道_malloc 在堆(内存)上创建 40 个字节并返回第一个内存地址,但我看不到堆栈是如何填充的,除了从它获取的指令本身之外,这里没有与内存的交互.
【问题讨论】:
-
"$5, -8(%rbp) - 没有引用..." - 好吧,那么猜猜 "-8(%rbp)" 是什么,这是对堆栈区域和值的引用 @ 987654331@ 正在存储到内存中。 (这是 AT&T 语法)...
a的内存地址是rbp-8。顺便说一句,这是特定于编译器实现的所有内容,并且特定于编译选项,因此编译器可以决定以完全不同的方式编译它(但您没有打开优化,因此默认情况下它使用 for-debugging 方式,将所有值放入内存调试器可以在“watches”窗口和类似窗口中找到它们) -
你提到了
memory address of a is rbp-8,如果我使用像 GDB 这样的调试器并实际打印出值,我会以0x7ffeefbffac8为例。什么是将此地址分配给a?操作系统? -
当进程启动时,它会从操作系统获取初始堆栈空间(由
rsp寄存器指向)......从那里通过 CRT 初始化代码等......你最终会到达你的代码,其中rbp相对于当前rsp设置,subq $16, %rsp保留16字节的堆栈空间,a也将降落,所以基本上是的,操作系统分配堆栈地址和所有然后基于堆栈的操作从该初始地址派生。 (下次运行可能会有所不同......同样,如果启用了 ASLR 并且您的可执行文件是 PIE,则代码本身可能位于其他位置) -
函数入口时
a与当前堆栈位置的相对位置然后由编译器在编译时分配,即它计算出a至少需要4个字节,对于a至少需要8个字节b,4 个字节作为返回值,总共 16 个字节,然后它会对齐以确保堆栈指针将按照 ABI 的要求对齐(在这种特殊情况下再次获得 16),因此它确实会生成执行 rbp= 的代码rsp, rsp-=16 和 &a=rbp-8 .. 这是编译时任务。它将落在物理计算机内存中的位置取决于函数入口时的当前rsp。
标签: c++ assembly stack heap-memory