c/c++ 内存管理与分配总结

来源:互联网 发布:win7美化桌面软件 编辑:程序博客网 时间:2024/05/17 23:40

                    
   如果你是学java的,那么你很幸运,你就不会为了解内存和分配管理内存而绞尽脑汁了,前几天学习了内存管理,和很多朋友讨论了这个问题,大家有很多疑惑的的问题,今天算是做个总结吧。
   计算机硬件系统主要有五大基本结构组成,运算器,控制器,存储器,输入设备,输出设备。
   通常我们说的CPU(中央处理器)是有运算器和控制器组成的。

   今天主要是谈谈存储器,我们现在写程序用的C或c++都是高级语言,但是电脑是个机器,它只认识它自己的机器语言,我一直都很尊敬IT的先驱们,它们用所谓的机器语言1和0和计算机对话,让我一想就很痛苦
   随着技术的发展,出现了一门提高编程和可读写性很好的汇编,是一种机器语言的助记符吧,最后汇编还是通过软件翻译成1和0,大学里有两门功课学到汇编,但到现在都没有太弄懂它,
   还好我敢上了高级语言的出现的年代,那么高级语言有怎么和机器交流的呢,首先C程序(高级语言) --(编译)->翻译成汇编语言->目标程序—-链接->执行程序
   就那linux下GCC举个例子吧!
   1预处理:gcc -E 把头文件编译进来,  预处理后停止,不进行编译过程。
   2编译: gcc -S  把c语言代码编译成汇编代码后停止编译,产生扩展名为.s的汇编文件
   3汇编:gcc -c 编译或汇编源文件,不连接,把.s文件转成目标文件(.o 文件)这样就完成了本人认为的翻译工作,因为目标文件里是二进制目标代码
   4链接:链接完成后就生成执行文件了。
   
   现在我们了解了高级语言和机器的交流方式了,下面谈谈是怎样管理和分配的!
   
   首先认识它存储器的层次结构  寄存器--->缓存---->主存---->磁盘--->外部存储器 存储速度是从快到慢,价格贵到便宜,容量从大到小。
   接下来了解几个概念 
 物理地址:
  内存由若干存储单元组成,每个存储单元有一个编号,称为物理地址。
 物理空间:
  物理地址的集合称为物理空间。
 逻辑空间:
  源程序经过编译后,形成目标程序。每个目标程序都是以0为基址进行顺序编址,这样的目标程序占据一定的地址空间,称为逻辑空间。
 逻辑地址:
  逻辑空间中每条指令的地址和指令中要访问的操作数地址统称为逻辑地址。
  32位机每个程序最大的逻辑地址空间为4G=232。
 虚拟内存:
  是指计算机呈现出要比实际拥有的内存大得多的内存量。因此他允许程式员编制并运行比实际系统拥有的内存大得多的程式。这使得许多大型项目也能够在具有有限内存资源的系统上实现

通常人们会把逻辑地址叫做虚拟地址,因为逻辑地址对于物理地址来说是”不存在的“或是”想象出来的“
有些人问编译时占内存吗!其实是不占物理地址也可说不占物理内存,但写程序的是时候就分配了内存呀,因为一个编译好的程序存于它自己的逻辑地址空间中,运行时,要把它装入内存空间,便具有了相应的物理地址。
也就形成了人们常说的程序只有执行时才占内存额说法,因为逻辑地址相对于物理地址是不存在的,而物理地址真真正正的在机器上的。

内存分配:
文字常量区——存放常量字符串,程序结束后自动释放
代码区—— 存放函数体的二进制代码。
全局区(静态区—— 存放常量,全局变量和静态变量(static).初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.程序结束后由系统释放.
栈区——由编译器自动分配、释放,存放函数的参数值,局部变量,本地变量的值等。
堆区——由程序员手动分配、释放,若不释放,则会造成内存泄漏。

二、例子程序  
网上的一个例子
//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 "优化成一个地方。  
}  
 
 程序的装入:
   程序以进程形式在内存运行,所以程序运行时就要将程序代码和数据装入内存。
(1)绝对装入方式
适用于单道程序环境
程序每次装入内存的起始位置是固定的,所以编译程序可以产生含绝对地址的目标代码。该目标代码装入内存后,可直接根据绝对地址进行存取,不需要进行逻辑地址到物理地址的转换。
(2)可重定位装入方式
可重定位装入方式
可用于多道程序环境
可执行程序中的地址 通常将0作为起始地址。
可执行程序整体装入 内存,同时确定目标 程序在内存中的起始 地址。
在程序装入时对可执行 程序中指令和数据的 地址进行修改的过程 称为重定位(静态重定位
(3)动态运行时装入方式
可用于多道程序环境
方式2不允许程序运行时在内存中移动位置。
实际上运行时程序在内存中的位置要经常改变,所以要采用动态运行时装入方式。
把可执行程序装入内存后,并不立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。
  程序的链接:
    目标程序经过链接生成可执行文件,与目标程序中使用的库函数有关系。
根据链接时间不同,分为:
(1)静态链接

静态链接
将可执行程序与它们所需要的库函数链接成一个完整的可执行程序。

(2)装入时动态链接

装入时动态链接
可执行程序执行时,装入内存时边装入边链接。
装入时,如果该程序调用库函数,则将相关库函数所在模块装入内存。

(3)运行时动态链接
对方式2的改进,近几年流行。
方式2只要调用库函数,就装入相关模块。但是并不是所有的库函数都在一次执行过程中都运行的。
运行时动态链接是指在程序执行过程中调用库函数,再将相关模块装入内存,链接到执行的程序的。
加快程序装入过程,节省内存空间。

(这个才是现在的主流,很多.dll文件都是这样,在程序运行的时候需要了什么模块就去找什么模块,
叫OS把它装入内存,这个与与上面的装入时的动态链接可有区别,上面的那个是在装入程序的时候去加载所需模块,
但实际上程序运行的时候所需的模块比装入的模块少多了,
因为上面装入的时候是可能需要的模块。但是采用运行时动态加载就指定了所需什么模块就加载什么模块。节省了装入的时间,也节省了磁盘空间。)


程序执行时,要占用一定内存,将内存分配给程序主要有以下几种方式
连续分配方式  
基本分页存储管理方式 
基本分段存储管理方式 
请求分页存储管理方式 
请求分段存储管理方式

原创粉丝点击