图中给出了在一个典型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. 转:oracle几组重要的常见视图-v$segstat,v$segment_statistics,v$filestat,v$rollstat

    v$segstat 本视图实时监控段级(segment-level)统计项,支持oracle9ir2及更高版本 V$SEGSTAT中的常用列 TS#:表空间标识 OBJ#:字典对象标识 DATAOBJ ...

  2. AD芯片的基准参考电压问题

    基准参考电压的精度一般非常高的! AD芯片 : AD9226的基准参考电压  误差一般是  千分之一! 我之前用万用表测量AD9226的参考电压大概是1.89V(这款AD的正确参考电压应该是2V),所 ...

  3. 关于 object-c的@protocol的理解

    从java角度来理解 @protocol 相当于 java 的接口定义,用法也一样 下面是试验例子 @protocol mytestClass <NSObject> - (void) ca ...

  4. Linux性能监测:内存篇

    在操作系统里,虚拟内存被分成页,在 x86 系统上每个页大小是 4KB.Linux 内核读写虚拟内存是以 “页” 为单位操作的,把内存转移到硬盘交换空间(SWAP)和从交换空间读取到内存的时候都是按页 ...

  5. 生成signature

    签名规则说明 1. 将参数先按键值排序(只做一级排序),进行key和value的拼接 2. 拼接完后,在最后面再拼接上分配的 appSecret 然后用sha1对拼接串加密 签名示例 假如传入参数: ...

  6. vue 构建前端项目并关联github

    这几天尝试用node开发一个网站,后端的接口已经初步开发完成,现在开始构建前端的项目,记录下过程,在学习下吧. 用vue-cli 构建项目,myproject.(构架过程略过) 每次在本地构建项目后和 ...

  7. python动态爬取网页

    简介 有时候,我们天真无邪的使用urllib库或Scrapy下载HTML网页时会发现,我们要提取的网页元素并不在我们下载到的HTML之中,尽管它们在浏览器里看起来唾手可得. 这说明我们想要的元素是在我 ...

  8. android 命名规则

    包名结构: 资源命名方式:

  9. 【bzoj1834】[ZJOI2010]network 网络扩容

    1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2701  Solved: 1368[Submit ...

  10. HTML5样式、链接和表格

    -------------------siwuxie095 HTML5 样式 1.标签 <style> 标签:样式定义 <link> 标签:资源引用 2.属性 rel:用于指定 ...