c++程序的内存模型

来源:互联网 发布:h1 seo 编辑:程序博客网 时间:2024/05/16 08:49

本文说的是C++程序(非对象的)的内存模型。分成内存布局和虚拟内存两部分来说明。


1、程序内存布局

C++内存模型图如下:


图三:C++程序的内存布局

从上面的图示中,可以建立起对C++程序运行的一个整体认识,共分为栈、堆、全局数据区和程序代码区。文首程序中的变量a便是局部变量,所以被分配在栈区。大家回忆一下栈的知识,一个栈顶指针,栈从高地址向低地址生长,栈有大小限制。


针对一个简单的程序:

#include<iostream>

#include<string>

usingstd::string;

usingstd::cout;

usingstd::endl;

 

int global_a = 5;     //全局对象

static int global_b = 6;  //全局静态对象

 

intmain()

{

      int a = 5; //声明一个变量5

      char b = 'a';

      int c = 8;

      static int d = 7;

      cout<<&a<<endl;                 

      cout<<&c<<endl;

      cout<<&d<<endl;

      cout<<&global_a<<endl;

     cout<<&global_b<<endl;

     return 0;

}

运行结果(Vc6.0):



 首先,在VC++6.0下,在“a=5”这行语句上加一个断点,按F5j进入调试模式,然后按ALT+8查看编译器生成的汇编代码:



 这里面ebp就是当前的栈顶地址,注意到a占用三个字节,而且答应出来的a的地址实际上是a低字节的地址(回忆下计算机组成结构的知识)。那好,在第一个数据a入栈前的栈顶指针是多少呢?计算一下0012FF7C+4=0012FF80,这个值是由编译器决定的,从上面可以看出,VC++6.0下和VC++2008下这个值是不一样的。同理,也可以计算出全局数据区的起始地址。

好的,我们在深入一下,讨论另外一个问题,内存对齐。在声明变量a之后,又接着声明了字符b(占一个字节),最后又声明了整形变量c,那么c的地址应该是多少呢?计算一下:0012FF7C-1-4=0012FF77。但是实际上打印出来的是0012FF74。为什么呢?答案就是内存对齐。

在现代计算机体系结构中,为了使CPU对变量进行高效、快速地访问,变量的地址应该具有某种特性,那就是“对齐”,对齐行为是有编译器来来实施的。如本例中对于4个字节大小的整形变量,其起始地址应该位于4个字节边界上,即能够被4整除,汇编上的黑话叫做“模四地址”。

现在我们修改一下文首的代码,将动态对象加入,注意到动态对象是在堆中分配的,增加下面几行代码:

int * pinteger = new int(5);   // 在堆上分配内存

cout<<pinteger<<endl;

int * pinteger2 = new int(5);

cout<<pinteger2<<endl;

delete pinteger;

delete pinteger2;

增加了堆中的内存分配,输出结果:


注意到最后打印出的两个变量地址就是堆上的地址,注意到两个地址不是连续增长的。而在栈上和全局数据区分配的内存地址则是连续的。再就是注意到堆的地址在栈和全局变量区之间,这和上面所示的C++内存布局图示是一致的。


2、虚拟内存

该例子编译后第一次执行和第二次执行以及重新编译再次运行的运行结果:



可见三次输出结果的对象的地址是一样的。

因为打印出来的地址不是变量的实际物理地址,而是虚拟地址,也叫逻辑地址。三个程序在操作系统中被当成三个独立的进程,彼此拥有独立的地址空间。它们之间互不影响,比如同样地址为0012FF7C的内存,在不同的进程中,它们的数据可能是完全不同的(在这个例子中是相同的)。通过虚拟内存机制,为每个进程提供了一个一致的内存视图,同时使得逻辑内存与物理内存分隔开来。 这样,就算变量虚拟地址相同的也不会冲突。




0 0
原创粉丝点击