决胜C++面试:堆、栈面面观

本文探讨了C++中堆和栈的概念,解释了它们在内存管理中的作用。栈遵循FILO原则,而堆则涉及动态内存分配。函数参数的传递可能涉及寄存器和栈,具体依赖于参数类型和数量。此外,文章还介绍了堆的分配策略,如首次适应算法等,并简述了虚拟地址、MMU和共享库的基本概念。
摘要由CSDN通过智能技术生成

        学习C语言,我们都听过堆(heap)和栈(stack)的概念。需要注意的是:有些地方“堆栈”这个词特指的是栈,而不是堆和栈。命名约定:本文中堆栈一次出现的地方,指的是两种东西,而非一种。

        在数据结构中,我们也听过栈和堆这两种数据结构,当然和我本文要讲的东西是不同的概念。不过数据结构中的栈(算法、数学意义上的一种抽象),和本文中的栈(实际存在的存储区)有一共同之处就是FILO —— 先入后出。但是数据结构中的堆和我们本文中的堆则是毫不相干

无图言卵


        注意,这里指的都是虚拟内存空间,并不是实际的物理内存布局。每一个进程都有自己的虚拟内存空间,也就是说我们程序中指针所表示的地址实际是虚拟地址,而不是物理地址。


增长方式

        所谓地址增长方式,是指堆或栈在分配内存的时候,其分得的内存空间的地址的增长方式。堆的地址增长方式是 从低到高的,而栈的地址增长方式是 从高到低的。(在说这句话的时候,要明确的一点就是这是指的x86系统,并非所有架构的系统都如此)
之所以它们会呈现出相反的两种地址增长方式是有其历史原因的:
-------------------------------------------------------------------------------------------------------
        早期的机器上内存的大小十分有限,如果堆和栈使用相同的地址增长方式(比如,都是从低到高),假设,一段内存空间的起始位置(先忽略其他段)设置为堆的起始地址,那么栈的起始地址必然在这段内存空间的中间某处,这个起始点如果选择得太靠后,则可能导致栈的内存大小不够,太靠前则会导致堆在分配内存的时候,可能与栈地址冲突。这时x86系统的设计师们,巧妙地将栈的起始位置定于内存空间的另一端,而其地址增长方式也是和堆相反的从高到低,这样从一定程度上,减轻了堆栈内存地址冲突或栈空间不足的可能性。就好比两个小伙伴在同一本笔记本上记笔记,两个人分别从笔记本的首页和尾页开始写,其笔记冲突的概率大大减小。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

函数栈

        栈与函数有莫大的关系。简单说来,我们在函数中声明的任何局部变量(非静态)都是在栈中分配的(编译期间完成)。并且函数的参数,以及返回值也是依赖于栈。
        为了深入地探讨这些概念,我们需要从汇编角度来展开研究。在使用gcc编译的时候,-S选项可以生成汇编代码。但此时生成的汇编代码是AT&T风格的,我们可以用-masm=intel生成intel风格的汇编。比如:gcc -S -masm=intel hello.c 这时就会生成汇编文件hello.s。请注意我们此时探讨的真相都是不开编译器优化选项的,因为如果开了编译器优化选项,那么其汇编行为往往已经完全不是我们代码本来的执行细节了。(编译器保证的是最终一致性,为了优化可能会改变我们的程序的细节)

变量

        看一个简单的例子:
int main()
{
    int a;
    int b = 1;
    int c = 2;
}
        其通过gcc -S -masm=intel汇编之后的汇编代码 主要部分如下,注意不同版本的gcc编译器,不同位数的操作系统(32位或64位)其汇编代码可能不同,但大致的意思相同。
main:
.LFB0:
	.cfi_startproc
	push	rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	mov	rbp, rsp
	.cfi_def_cfa_register 6
	mov	DWORD PTR [rbp-8], 1
	mov	DWORD PTR [rbp-4], 2
	pop	rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
        可以忽略掉其中点号. 开头的语句。再看一遍:
main:
.LFB0:
	push	rbp
	mov	rbp, rsp
	mov	DWORD PTR [rbp-8], 1
	mov	DWORD PTR [rbp-4], 2
	pop	rbp
	ret
        因为我是64位系统,所以汇编代码中的寄存器都是r打头的(rbp,rsp)64位寄存器。x86架构(通常是32位系统)是e打头的(ebp,esp)。以下涉及到寄存器时,我通常会省略其前缀,而直接用bp和sp来称呼。bp是基址寄存器,sp是栈顶指针寄存器(sp的位置,是我们实际能使用的栈的大小)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
请自行区分操作系统位数和cpu架构位数的区别。x64(x86-64),x86是CPU架
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

果冻虾仁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值