图中给出了在一个典型c++程序中如何组织内存的框架。程序中的指令(在底层都是按位存储的)、全局变量、静态对象和只读常量往往被存储在静态去(static area)(第二个图中的数据段、代码段。值得注意的一点是:代码段中存储的是可执行的代码和只读常量,很多人看到代码段就认为里面只有代码,数据段里面才是存储数据的,其实不是这样的。),该区域位于地址编址号较小的接近机器地址空间的开始处。该区域所分配的内存量在程序运行期间不会发生改变。关于全局对象,是在main()函数执行前就分配好了的。其实,在main()函数中的显示代码执行之前,会调用一个由编译器生成的_main()函数,而_main()函数会进行所有全局对象的的构造及初始化工作。而在main()函数结束之前,会调用由编译器生成的exit函数,来释放所有的全局对象。比如下面的代码:

int main()

{

 … …// 显式代码

}

  实际上,被转化成这样:

int main()

{

 _main(); //隐式代码,由编译器产生,用以构造所有全局对象

 … … // 显式代码

 … …

 exit() ; // 隐式代码,由编译器产生,用以释放所有全局对象

}

刚才讲的是静态存储区中的全局对象,那么,局部静态对象了?局部静态对象通常也是在函数中定义的,就像栈对象一样,只不过,其前面多了个static关键字。局部静态对象的生命期是从其所在函数第一次被调用,更确切地说,是当第一次执行到该静态对象的声明代码时,产生该静态局部对象,直到整个程序结束时,才销毁该对象。

  还有一种静态对象,那就是它作为class的静态成员。考虑这种情况时,就牵涉了一些较复杂的问题。

  第一个问题是class的静态成员对象的生命期,class的静态成员对象在初始化静态成员对象就产生了,直到整个程序结束时,才销毁该对象。

  第二个问题是,当出现下列情况时:

#include<iostream>
using namespace std;
class a {public:static int* g; };
int* a::g = new int;
class b : public a{};
class c : public b{};
int main() {
cout << a::g << endl;
cout << b::g << endl;
cout << c::g << endl;
}

  输出结果

00E89F10
00E89F10
00E89F10

  继承类和基类共用一个静态成员变量。

  内存中的最高地址区表示栈区(stack area)。当你的程序每调用一个函数或者方法,计算机(编译器自动管理)就会在这个内存区创建一个新的栈帧。当函数返回时,所创建的栈帧会被撤销,以为后续的函数调用所需的栈帧释放内存。通常栈空间容量比较小,一般是1MB~2MB,可设置大小。栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。栈内存的分配是连续的,类似于平时我们所说的栈,如果还不清楚,那么就把它想成数组,它的内存分配是连续分配的,即,所分配的内存是在一块连续的内存区域内.当我们声明变量时,那么编译器会自动接着当前栈区的结尾来分配内存.栈向下,向低地址方向增长。对于栈,它是一个先进后出的队列,进出一一对应,不会产生碎片。只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

  处于栈区和静态区之间的内存区域被称为堆区(heap area)。(程序员手工管理)该区域会在程序运行时请求更多内存的时候发挥作用。唯一的方法是用new(malloc)。相比于栈空间,堆的容量要大得多。一般来讲在32位系统下,堆内存可以达到4G的空间。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块 内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的 大小,系统会自动的将多余的那部分重新放入空闲链表中。频繁的new/delete会造成大量碎片,使程序效率降低。堆向上,向高地址方向增长。用malloc 或new 申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL 的内存。

内联函数的临时变量存在main的栈上,相当于main的临时变量。

这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = “abc”; 栈
char *p2; 栈
char *p3 = “123456”; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配 得来得10和20字节的区域就在堆区。
strcpy(p1, “123456”); 123456\0放在常量区,编译器可能会将它与p3所指向的”123456”优化成一个地方。
}

  

c++程序设计基础、编程抽象与算法策略

另有说法是5个区

作者:Acjx

链接:https://www.zhihu.com/question/26224882/answer/32384814
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1.在网上查资料看到:两种说法,一种是分为 栈,堆,自由存储区,全局,常量;第二,堆,栈,全局,常量,程序代码区,到底哪一个是对的呢?我个人一直是理解堆和自由存储区是一种东西,请解答一下疑惑

2,另外一个问题:类的成员函数,存放在什么地方呢?

第一个问题,如下:
1 .rodata段:存放只读数据,比如printf语句中的格式字符串和开关语句的跳转表。也就是你所说的常量区。

例如,全局作用域中的 const int ival = 10,ival存放在.rodata段
再如,函数局部作用域中的printf("Hello world %d\n", c);语句中的格式字符串"Hello world %d\n",也存放在.rodata段

2 .text段:存放已编译程序的机器代码。

注意:程序加载运行时,.rodata段和.text段通常合并到一个Segment(Text Segment)中,操作系统将这个Segment的页面只读保护起来,防止意外的改写。

3 .data段:存放已初始化的全局变量。而局部变量在运行时保存在栈中,既不出现在.data段,也不出现在.bss段中。就是你所说的全局区。

例如:全局作用域中的int ival = 10,static int a = 30,以及局部作用域中的static int b = 30,这3个变量均存放在.data段中。注意,局部作用域中的static变量的生命周期和其余栈变量的生命周期是不同的。

4 .bss段:存放未初始化的全局变量。在目标文件中这个段不占据实际的空间,它仅仅是一个占位符,在加载时这个段用0填充。目标文件区分初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。全局变量如果不初始化则初值为0,同理可以推断,static变量(不管是函数里的还是函数外的)如果不初始化则初值也是0,也分配在.bss段。

例如,全局作用域中的int ival; ival显然存放在.bss段

注意:.data和.bss在加载时合并到一个Segment(Data Segment)中,这个Segment是可读可写的。

5. 栈:函数的参数和局部变量是分配在栈上(但不包括static声明的变量)。在函数被调用时,栈用来传递参数和返回值。由于栈的后进先出特点,所以栈特别方便用来保存/恢复调用现场。

6. 堆:用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)

现在回答你第二个问题,类的成员函数存放在代码段。

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。注意要考虑对齐。

 
 
 
 典型的C程序之内存布局看起来是这样子滴,【图片来源:http://www.slideshare.net/emersonferr/2-c-reviewi】


内存布局------c++程序设计基础、编程抽象与算法策略的更多相关文章

  1. 声明函数指针、回调函数、函数对象------c++程序设计基础、编程抽象与算法策略

    声明函数指针 #include<iostream> using namespace std; double a(double aa) { return aa; } int main() { ...

  2. Anatomy of a Program in Memory.剖析程序的内存布局

    原文标题:Anatomy of a Program in Memory 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译 ...

  3. 探讨C++ 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别

    探讨C++ 变量生命周期.栈分配方式.类内存布局.Debug和Release程序的区别(一) 今天看博客园的文章,发现博问栏目中有一个网友的问题挺有趣的,就点进去看了下,标题是“C++生存期问题”,给 ...

  4. C语言程序内存布局

    C语言程序内存布局 如有转载,请注明出处:http://blog.csdn.net/embedded_sky/article/details/44457453 作者:super_bert@csdn 一 ...

  5. ESP32应用程序的内存布局

    应用程序内存布局 ESP32芯片具有灵活的内存映射功能.本节介绍ESP-IDF在默认情况下如何使用这些功能. ESP-IDF中的应用程序代码可以放置在以下内存区域之一中. IRAM(指令RAM) ES ...

  6. UNIX高级环境编程(8)进程环境(Process Environment)- 进程的启动和退出、内存布局、环境变量列表

    在学习进程控制相关知识之前,我们需要了解一个单进程的运行环境. 本章我们将了解一下的内容: 程序运行时,main函数是如何被调用的: 命令行参数是如何被传入到程序中的: 一个典型的内存布局是怎样的: ...

  7. 一起talk C栗子吧(第一百三十一回:C语言实例--C程序内存布局三)

    各位看官们,大家好.上一回中咱们说的是C程序内存布局的样例,这一回咱们继续说该样例.闲话休提,言归正转.让我们一起talk C栗子吧. 看官们,关于C程序内存布局的样例,我们在前面的两个章回都介绍过了 ...

  8. 用一个词(TASPK)牢记C程序内存布局

    一个典型的C程序内存布局,从低地址到高地址分别为: 1. text (正文段,即代码段 Code Segment) 2. data (已经初始化的数据段) 3. bss (未被初始化的数据段 Bloc ...

  9. C语言程序的内存布局

    C语言程序的内存布局 一:C语言程序的存储区域 C语言编写的程序经过编绎-链接后,将形成一个统一的文件,它由几个部分组成,在程序运行时又会产生几个其他部分,各个部分代表了不同的存储区域: 1.代码段( ...

随机推荐

  1. PHP传入数组到js

    1.方式一,处理成字符串 js再将字符串处理成数组. var spec1_default = "{$spec1_default}"; spec1_default = spec1_d ...

  2. VC++使用TCHAR

    #ifdef _UNICODE #define tcout wcout #define tcin wcin #else #define tcout cout #define tcin cin #end ...

  3. mysql整数类型

    数值类型 1.整数类型 整型类型的后面的宽度,不是存储宽度,是显示宽度,不够位数用0添加,够位数使用原数据 整数类型:TINYINT SMALLINT MEDIUMINT INT BIGINT 作用: ...

  4. Django Rest Framework 4

    目录 一.分页 二.视图 三.路由 四.渲染器 一.分页 试问如果当数据量特别大的时候,你是怎么解决分页的? 方式a.记录当前访问页数的数据id 方式b.最多显示120页等 方式c.只显示上一页,下一 ...

  5. mahout in Action2.2-给用户推荐图书(2)-分析对用户推荐书目的结果

    2.2.3 Analyzing the output 在之前的程序运行结果中我们得到的结果输出是: RecommendedItem [item:104, value:4.257081] 程序要求选择一 ...

  6. FPGA---ucf文件语法

    1.约束文件的概念 FPGA设计中的约束文件有3类:用户设计文件(.UCF文件).网表约束文件(.NCF文件)以及物理约束文件(.PCF文件),可以完成时序约束.管脚约束以及区域约束.3类约束文件的关 ...

  7. POJ1039几何

    这道题目要求我们判断光线进入一条管道后可以抵达的最大的x坐标. 这是我做的第一道几何题目,卡了我半天.翻了不少书,才大概明白了些.既然是第一次做,就把所有今天学到的就全部写下好了. 1.如何判断平面上 ...

  8. 简单的互斥同步方式——synchronized关键字详解

    目录 1. 关于synchronized关键字 2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 2.2 synchronized是如何实现线程互斥访问 ...

  9. opennebula 对接创建模板参数

    { "id": 8, "name": "c5d1390c-1930-45a5-a686-5cef38b319d7", "displ ...

  10. ROS 下使用3D激光雷达 velodyne vlp-16

    Velodyne VLP16型激光雷达横向视角360°,纵向视角30° 系统和ROS版本:Ubuntu 14.04 ,ros indigo 1. 安装驱动 sudo apt-get install r ...