编译时和运行时分别做了哪些动作

编译时和运行时的内存分配

1. 程序编译时

  • 代码段(Text Segment):存放可执行指令,编译器将源代码编译为机器码并确定其位置。

  • 数据段(Data Segment):存放全局变量和静态变量,编译器分配内存并确定初始值。

  • BSS段(Block Started by Symbol):存放未初始化的全局变量和静态变量,编译器分配内存但不初始化。

2. 程序运行时

  • 堆(Heap):动态分配内存,通过malloccallocrealloc等函数在运行时分配,大小不固定。

  • 栈(Stack):存放局部变量和函数调用信息,由编译器自动管理,遵循LIFO原则。

  • 内存映射段(Memory Mapping Segment):用于映射文件或匿名内存,如动态库和mmap分配的内存。

总结

  • 编译时:确定代码段、数据段和BSS段的内存布局。

  • 运行时:动态管理堆、栈和内存映射段的内存分配。

这些步骤共同确保程序在编译和运行时能正确分配和使用内存。

栈内存的管理是如何实现的?

在C++中,函数调用时的压栈(参数传递、返回地址和局部变量分配)以及栈内存回收是由编译器生成的代码管理的,具体过程如下:

1. 压栈操作

  • 编译器生成指令:编译器在编译阶段会插入机器指令,用于在函数调用时将参数、返回地址和局部变量压入栈中。例如:

    • 参数按调用约定(如cdeclstdcall)压栈(顺序可能不同)。

    • 返回地址(函数执行完后跳转的位置)由call指令自动压栈。

    • 局部变量通过调整栈指针(如sub esp, N)在栈上分配空间。

  • 硬件执行:程序运行时,CPU根据这些指令实际修改栈指针(如ESP/RSP),完成内存操作。

2. 栈内存回收

  • 自动回收:函数返回时,编译器生成的代码会恢复栈指针(如add esp, Nmov esp, ebp),释放局部变量和参数占用的栈空间。

    • 清理工作可能由调用者或被调用者负责,具体取决于调用约定(例如:cdecl由调用者清理参数,stdcall由被调用者清理)。

  • 生命周期管理:栈内存的回收仅通过移动栈指针实现,内存内容不会主动清除,但后续函数调用会覆盖该区域。

3. 编译器与运行时的分工

  • 编译器:负责生成正确的压栈和回收指令。

  • 运行时:CPU在程序执行期间动态执行这些指令,维护栈指针。

4. 关键特点

  • 自动性:无需手动管理,由编译器保证栈的完整性。

  • 高效性:栈分配/回收仅需移动指针,速度极快。

  • 作用域绑定:局部变量生命周期严格匹配函数作用域。

示例(x86汇编片段):

; 函数调用时:
push arg1      ; 压入参数(编译器决定顺序)
call func      ; 压入返回地址并跳转

; 函数内部:
push ebp       ; 保存旧栈帧基址
mov ebp, esp   ; 建立新栈帧
sub esp, 16    ; 分配16字节局部变量空间

; 函数返回前:
mov esp, ebp   ; 回收局部变量空间
pop ebp        ; 恢复旧栈帧基址
ret            ; 弹出返回地址并跳转

; 调用者清理参数(cdecl约定):
add esp, 4     ; 清理压入的arg1

总结

栈内存管理由编译器生成的代码控制,运行时通过调整栈指针自动完成。这是C++高效函数调用的核心机制之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值