请描述C/C++程序的内存分区
其实c和c卄的内存分区还是有一定区别的,但此处不作区分:
(1)栈区(stack):
- 由编译器进行管理,在需要时由编译器自动分配空间,在不需要时自动回收空间,一般保存的是局部变量和函数参数等。
- 一般来说,CPU有专门的指令可以用于入栈和出栈的操作。当一个函数被调用时,就会有指令把当前指令的地址压入栈内保存起来,然后跳转到被调用的函数执行。函数返回的时候,就会把栈里面的指令地址弹出来继续执行。
- 它是连续的内存空间,在函数调用的时候,首先入栈的是下一条可以执行指令的地址,然后是函数逇各个参数
- 大多数编译器中,参数是从右向左入栈(原因在于采用这种顺序,可以让程序员在使用C/C++的“函数参数长度可变”这个特性时更方便。如果从左向右压栈,第一个参数(即描述可变参数表各个变量的那个参数)将被放在栈底,由于可变参数函数的第一步就需要解析可变参数表的各个参数类型,即第⼀步就需要得到上述参数,因此,将它放在栈底是很不⽅便的。)
- 本地函数调用结束时,局部变量先出栈,然后是参数,最后是栈顶指针最开始存放的地址,程序由该点继续运行,不会产生碎片
- 栈是⾼地址向低地址扩展,栈低⾼地址,空间较⼩。
- 栈操作很快:
- 数据存取的位置总是在栈顶而不需要寻找位置存放获取读取数据
- 栈中的所有数据必须占用已知且固定的大小
- 调用函数时,传递给函数的值[包括指向对上数据的指针]和函数的局部变量被压入栈中。
(2)堆区(heap):
- 由程序员管理,需要⼿动 new malloc delete free 进⾏分配和回收,如果不进⾏回收的话,会造成内存泄漏的问题。
- 在编译时大小未知或者大小可能变化的数据,要存储到堆上
- 堆是为动态分配预留的内存空间,是不连续的,每个线程都有一个栈,但是每一个应用程序只有一个堆。
- 堆是缺乏组织的:当向堆放入数据时,需要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并且返回一个表示该位置地址的指针。这个过程称为在堆上分配内存
- 返回的堆地址指针存放在栈中[因为指针的大小是已知并且固定的]
- 实际上系统中有一个空闲链表,当有程序申请的时候,系统遍历空闲链表找到第一个大于等于申请大小的空间分配给程序,一般在分配程序的时候,也会在空间头部写入内存大小,方便delete回收空间大小。当然,如果有剩余的,也会将剩余的插入到空闲链表中,这也是产生内存碎片的原因
- 堆是低地址向⾼地址扩展,空间交⼤,较为灵活。
- 访问堆上的数据比访问栈上的数据慢:
- 因为必须通过指针来访问。现代CPU在内存中跳转越少就越快
- 在堆上分配大量的空间也可能消耗时间
- 注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
(3) 全局/静态存储区:分为初始化和非初始化两个相邻区域,存储初始化和未初始化的全局变量和静态变量,程序结束后由系统释放
(4)常量存储区:存储常量,比如字符串,一般不允许修改。程序结束后由系统释放
(5)代码区:存放程序的二进制代码
栈区与堆区的区别:
- 存储内容:栈存储局部变量、函数参数等。堆存储使用new、malloc申请的变量等
- 申请方式:栈内存由系统分配,堆内存由程序员申请
- 申请后系统的响应:
- 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出
- 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所省去空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序
- 申请大小的显示:WindowsT栈的大小一般是2M,堆的容量较大;
- 申请效率的比较:栈由系统自动分配,速度较快。堆使用new、malloc等分配,比较慢
总结:栈区优势在于处理效率,堆区优势在于灵活
内存模型:自由区、静态区、动态区
根据C/C++对象生命周期的不同,C/C++的内存模型有三种不同的内存区域,即:自由存储区、动态区、静态区
- 自由存储区:局部非静态变量的存储区,也就是平时说的栈
- 动态区:由new、malloc分配的内存,即平常所说的堆;
- 静态区:全局变量、静态变量、字符串常量存在的位置
注意:代码虽然占内存,但不属于C/C++内存模型的一部分;
C++是怎么定义常量的?常量存放在内存的哪个位置
- 对于局部常量,存放在栈区
- 对于全局常量,编译器一般不分配内存,放在符号表中以提高访问效率
- 对于字面值常量,比如字符串,一般常在常量区