
程序中动态分配的内存存放在堆内存区域。
叫“堆”。c语言:char p1; //若是全局量,则在全局未初始化区,若是局部量,则在栈中。p1 = (char )malloc(10); //分配得来得10字节的区域在堆区。
c++语言: 用 new分配,用 delete释放,在 堆区。用malloc分配,用 free释放,在类似堆区的自由存储区。堆不同于栈,它的数据结构并非由系统(无论是机器系统还是 *** 作系统)支持,而是由函数库提供的。
直接方式:
当时多道程序技术还没出现,存储器的可用空间一般是给定的。那时程序员在编程序时或编译程序对源程序进行编译时,使用实际的存储器地址,这种分配方式使用户与计算机内存直接打交道。
系统资源在某一时刻为一个用户所独占。当多道程序出现时就使用户感到极不方便,因为用户要自己做主存的分配工作,而且内存不止存放一个作业,这要求用户必须知道每一个作业放在主存的什么地方,这无疑增加了用户的负担,况且存储空间的利用率也相当低。
先从大家比较熟悉的栈说起,它是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。这就如同要取出放在箱子里面底下的东西(放入的比较早的物体),首先要移开压在它上面的物体(放入的比较晚的物体)。而堆就不同了,堆是一种经过排序的树形数据结构,每个结点都有一个值。通常所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列,堆的存取是随意,这就如同在图书馆的书架上取书,虽然书的摆放是有顺序的,但是想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,可以直接取出想要的书。
下面就说说C语言程序内存分配中的堆和栈,这里有必要把内存分配也提一下,一般情况下程序存放在Rom或Flash中,运行时需要拷到内存中执行,内存会分别存储不同的信息。
内存中的栈区处于相对较高的地址以地址的增长方向为上的话,栈地址是向下增长的,栈中分配局部变量空间,堆区是向上增长的用于分配程序员申请的内存空间。另外还有静态区是分配静态变量,全局变量空间的;只读区是分配常量和程序代码空间的;以及其他一些分区。来看一个网上很流行的经典例子:
maincpp
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); 堆
}
堆和栈的第一个区别就是申请方式不同:栈(英文名称是stack)是系统自动分配空间的,例如定义一个 char a;系统会自动在栈上为其开辟空间。而堆(英文名称是heap)则是程序员根据需要自己申请的空间,例如malloc(10);开辟十个字节的空间。由于栈上的空间是自动分配自动回收的,所以栈上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。而堆上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露。
EXE 文件比较复杂,每个EXE文件都有一个文件头,结构如下。
EXE文件头的信息
├偏移量┤ 意义 ┤
├00h-01h ┤MZ'EXE文件标记 ┤
├02h-03h ┤最后一页的字节数(每页512B) ┤
├04h-05h ┤文件长度(字节数)除以512的商+1 -| ┤
├06h-07h ┤重定位项的个数 ┤
├08h-09h ┤文件头除16的商 ┤
├0ah-0bh ┤程序运行所需最小段数 ┤
├0ch-0dh ┤大 ┤
├oeh-0fh ┤堆栈段的段值 (SS) ┤
├10h-11h ┤sp ┤
├12h-13h ┤文件校验和 ┤
├14h-15h ┤IP ┤
├16h-17h ┤CS ┤
├18h-19h ┤ ┤
├1ah-1bh ┤ ┤
├1ch ┤ ┤
―――――――――――――――――――――――――
EXE文件包含一个文件头和一个可重定位程序映象。文件头包含MS-DOS用于加载程序的信息,例如程序的大小和寄存器的初始值。文件头还指向一个重定位表,该表包含指向程序映象中可重定位段地址的指针链表。文件头的形式与EXEHEADER结构对应:
EXEHEADER STRUC
exSignature dw 5A4Dh ;EXE标志
exExraBytes dw ;最后(部分)页中的字节数
exPages dw ;文件中的全部和部分页数
exRelocItems dw ;重定位表中的指针数
exHeaderSize dw ;以字节为单位的文件头大小
exMinAlloc dw ;最小分配大小
exMaxAlloc dw ;最大分配大小
exInitSS dw ;初始SS值
exInitSP dw ;初始SP值
exChechSum dw ;补码校验值
exInitIP dw ;初始IP值
exInitCS dw ;初始CS值
exRelocTable dw ;重定位表的字节偏移量
exOverlay dw ;覆盖号
EXEHEADER ENDS程序映象
包含处理器代码和程序的初始数据,紧接在文件头之后。它的大小以字节为单位,等于EXE文件的大小减去文件头的大小,也等于exHeaderSize的域的值乘以16。MS-DOS通过把该映象直接从文件拷贝到内存加载EXE程序然后调整定位表中说明的可重定位段地址。
定位表是一个重定位指针数组,每个指向程序映象中的可重定位段地址。文件头中的exRelocItems域说明了数组中指针的个数,exRelocTable域说明了分配表的起始文件偏移量。每个重定位指针由两个16位值组成:偏移量和段值。 为加载EXE程序,MS-DOS首先读文件头以确定EXE标志并计算程序映象的大小。然后它试图申请内存。首先,它计算程序映象文件的大小加上PSP的大小再加上EXEHEADER结构中的exMinAlloc域说明的内存大小这三者之和,如果总和超过最大可用内存块的大小。则MS-DOS停止加载程序并返回一个出错值。如果总和没超过最大可用内存块的大小,它便计算程序映象的大小加上PSP的大小再加上EXEHEADER结构中exMaxAlloc域说明的内存大小之和,如果第二个总和小于最大可用内存块的大小,则MS-DOS 分配计算得到的内存量。否则,它分配最大可用内存块。分配完内存后,MS-DOS确定段地址,也称为起始段地址,MS-DOS从此处加载程序映象。如果exMinAlloc域和exMaxAlloc域中的值都为零,则MS-DOS把映象尽可能地加载到内存最高端。否则,它把映象加载到紧挨着PSP域之上。接下来,MS-DOS读取重定位表中的项目调整所有由可重定位指针说明的段地址。对于重定位表中的每个指针,MS-DOS寻找程序映象中相应的可重定位段地址,并把起始段地址加到它之上。一旦调整完毕,段地址便指向了内存中被加载程序的代码和数据段。 MS-DOS在所分配内存的最低部分建造256字节的PSP,把AL和AH设置为加载 COM程序时所设置的值。MS-DOS使用文件头中的值设置SP与SS,调整SS初始值,把起始地址加到它之上。MS-DOS还把ES和DS设置为PSP的段地址最后,MS-DOS从程序文件头读取CS和IP的初始值,把起始段地址加到CS之 上,把控制转移到位于调整后地址处的程序。
内存存放的数据分几个区,不仅是存变量那么简单,如下:
1、栈区(stack):由编译器自动分配和释放
,存放函数的参数值、局部变量的值等,甚至函数的调用过程都是用栈来完成。其 *** 作方式类似于数据结构中的栈
2、堆区(heap)
:一般由程序员手动申请以及释放,
若程序员不释放,程序结束时可能由OS回收
。注意它与数据结构中的堆是两回事,分配方式类似于链表
3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,
未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放空间
4、文字常量区:常量字符串就是放在这里的。
程序结束后由系统释放空间
5、程序代码区:存放函数体的二进制代码
苹果的进程使用内存的基本单元是页。当一个进程加载进内存时,会把内存划分成页的内存方式。为什么要这么做呢?
用页内存的方式能最大化的使用内存,减少内存碎片。虽然连续分区方式也可以把内存碎片拼接起来,但是开销太大。分页存储管理将进程的逻辑地址分为若干个页,为各个页编号,0、1、2、3、4、5。。。。同时在物理内存上分配与页大小相同的存储块,并编号与之对应。
物理内存页面的生命周期有几种状态
1、Free 空闲,物理页面没有被虚拟内存页面引用
2、Active 活跃,物理页面正用于虚拟内存页面,最近被虚拟内存页引用过
3、Inactive 非活跃,物理页面正用于虚拟内存页面,最近没有被虚拟内存页面引用过
4、Speculative 投机,对可能的内存需求做了一个猜测是的内存分配(不是活跃状态也不是非活跃状态,有可能马上被访问)
5、Wired down 联动,物理页面正用于虚拟内存页面,该页面被锁定,不能交换
我们都知道,系统同时会运行若干个进程。当我们要运行一个新的进程的时候,这个进程需要大量的内存,但是现有的内存空间不够用,这时系统会做什么呢?和页又有什么关系呢?
swap交换空间,当系统要加载一个进程的时候,发现内存不够用的时候,系统就会把Free页面回收,Inactive状态的页面交换到磁盘上,这时就会空出大量空间,苹果有一个进程dynamic_pager专门用来处理交换请求。如果内存还是不够用,iOS有一种压力释放机制叫VM,VM依赖于Jetsam。当内存中有大量驻留页面,这时候App会收到didReceiveMemoryWaining方法释放内存空间。这时候App有可能被Jetsam杀掉。
栈 alloca()
在栈上动态分配内存使用的是alloca()函数,这个函数和malloc()函数的原型是一样的,不同的是alloca()返回的是栈上的指针,malloc()返回的是堆上的指针。
栈上分配空间只不过是修改栈指针寄存器,而堆需要遍历空间找一个合适的空间要快的多。页面错误,栈基本上不会发生,因为栈已经加载到内存中了。而堆会有页面错误,尽管用户感受不到,但是它已经影响了性能。
栈分配了大量内存,在函数返回时,会自动回收。而堆很容易忘记,malloc之后,没去free。
堆 malloc()
堆的数据结构是二叉堆,但是现在的 *** 作系统都有自己的堆结构,而且更为复杂。苹果采用的是区(zone)的分配方式。通过这种方式,不用直接在页上去分配内存。
分配一个新的区域只需调用malloc_create_zone或NSCreateZone函数。一个区是什么结构呢?来看一下结构体malloc_zone_t的内容:
从上往下看
reserved1、reserved2 是保留字段。
size 返回指针zone分配的空间大小
malloc 这个区域malloc的实现
calloc、valloc、free、realloc 同上
zone_name 这个区域的名字
batch_malloc 分配若干个缓冲区,大小一样
batch_free 释放一组缓冲区
introspect 这个区域的信息
memalign 2的n次幂对其的malloc
free_definite_size 释放指定字节的空间
MCS-51单片机的存储器编址方式采用与工作寄存器、I/O端口锁存器统一编址的方式。程序存储器和数据存储器空间好似相互独立的,各自有自己的寻址系统和控制信号,物理结构也不同。程序存储器为只读存储器(ROM),数据存储器为随机存储器(RAM)。
1、程序存储器常用来存放程序和表格常数。程序存储器以程序计数器PC作为地址指针,通过16位地址总线,可寻址的地址空间为64K,片内、片外统一编址。在程序存储器中有些特殊的单元在使用时应加以注意。其中一组特殊的单元是0000H~0002H单元,在系统复位之后,PC为0000H,单片机从0000H开始执行程序,该单元是系统执行陈故乡的起始地址,通常在该地址中存放一条跳转指令,而用户程序从跳转地址开始存放程序。另外一组特殊单元为0003H~0021AH,这40个单元被均匀的分为5份,其定义如下:
0003H~000AH:外部中断0的中断地址区
000BH~0012H:定时器/计数器0的中断地址区
0013H~001AH:外部中断1的中断地址区
001BH~0022H:定时器/计数器1的中断地址区
0023H~002AH:串行中断地址区
可见以上40个单元是专门用于存放中断处理程序的地址单元,中断响应后,按中断的类型自动转到各自的终端区去执行程序。从上面看出,每个终端服务程序只有8个字节单元,用8个字节来存放一个中断服务程序显然是不可能的。通常情况下好似在中断响应的地址区存放一条无条件转移指令,指向程序存储器的真正存放终端服务程序的空间去执行。
2、MCS-51单片机的数据存储器无论在物理上或者逻辑上都是分为两个地址空间,一个为内部数据存储器,访问内部数据存储器用MOV指令;另外一个为外部数据存储器,访问外部数据存储器用MOVX指令。8051内部有128个8位数据存储单元和128个专用寄存器单元,这些单元是统一编址的,专用寄存器只能用于存放控制指令数据。所以,用户能使用的RAM只有00H~7FH单元组成的128字节地址空间,可以存放读写的数据或者运算的中间结果;80H~FFH单元组成的高128字节地址空间的特殊功能寄存器(SFR)区,只能访问,而不能用于存放用户数据。片内RAM的低128字节还可以分成工作寄存器区,可位寻址区和一般RAM去3个区域。
详细请参考《单片机C语言入门》人民邮电出版社
以上就是关于程序中动态分配的内存存放在哪个区域全部的内容,包括:程序中动态分配的内存存放在哪个区域、C程序中如何使用堆栈、可执行程序的文件结构等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)