博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
程序的存储空间布局/堆与栈
阅读量:5238 次
发布时间:2019-06-14

本文共 3554 字,大约阅读时间需要 11 分钟。

由编译器自动分配释放管理。局部变量及每次函数调用时返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。新被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C函数可以递归调用。

需要由程序员分配释放管理,若程序员不释放,程序结束时可能由OS回收。通常在堆中进行动态存储分配。

 

非初始化数据段

通常将此段称为b s s段,这一名称来源于早期汇编程序的一个操作符,意思是“block started by symbol(由符号开始的块)”,未初始化的全局变量和静态变量存放在这里。在程序开始执行之前,内核将此段初始化为0。函数外的说明:long sum[1000] ; 使此变量存放在非初始化数据段中。

 

初始化的数据

通常将此段称为数据段,它包含了程序中需赋初值的变量。初始化的全局变量和静态变量存放在这里。例如,C程序中任何函数之外的说明:int maxcount = 99; 使此变量以初值存放在初始化数据段中。

 

正文段

C P U执行的机器指令部分。通常,正文段是可共享的,所以即使是经常环境指针环境表环境字符串执行的程序(如文本编辑程序、C编译程序、s h e l l)在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修改其自身的指令。

 

对于x86处理器上的Linux,正文段从0x08048000单元开始,栈底在0xC0000000之下开始(栈由高地址向低地址方向增长)。堆顶和栈底之间未用的虚拟空间很大。

 

Shellsize命令可以看到一个程序的正文段(text)、数据段(data)、非初始化数据段(bss)及文件长度.

[foxman@17:01:49 ]$size mydesign

   text    data     bss     dec     hex filename

  79210    1380     404   80994   13c62 mydesign

 

堆与栈的区别由以下几点:

 

    管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak

    空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,可以修改:   

    打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit

注意:reserve最小值为4Bytecommit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

 

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

 

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

 

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

 

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

 

    从这里可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

    虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生,因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果.

 

来源:

1.       《UNIX环境高级编程》第3版

2.       http://www.52blog.net/user1/3843/archives/2004/54340.shtml

:转载时请以超链接形式标明文章原始出处和作者信息及

注:

/*程序执行/函数调用过程:

函数调用时:

建立新栈帧,传递参数,修改“当前代码行”;

函数退出时:

删除栈帧,处理返回值,修改“当前代码行”

*/

过程调用 

call   首先将被调函数的参数入栈,最后是返回地址入栈,再跳到被调函数起始地址
leave  准备返回时的桢栈 : 令栈指针指向先指向当前桢的起始处(这里保存的是调用者桢的起始地),出栈(桢指针重置为调用者桢的起始;且栈指针指向返回地址)

       等同于 :   

       movl %ebp,%esp 
       popl %ebp

ret    (栈指针指向返回地址)出栈跳到那个位置(返回地址).

 

程序栈的布局:

2010052715373356.png

 

关于寄存器

caller save:%eax,%edx,%ecx

callee save:%ebx,%esi,%edi

因为caller负责保存了%eax,%edx,%ecx,所以callee才可以直接用.

而caller却不负责保存%ebx,%esi,%edi,故callee想用的话必须先将其保存,才能使用.

所谓使用,即对其覆盖它原来的值

所谓保存,即入栈

(src: )

段错误和栈溢出:

 编译后的可执行文件都包含什么内容呢?对于Linux/Unix系统来说,其可性文件采用ELF格式,DOS系统采用COFF格式,Windows采用PE文件格式(COFF格式的扩展)。这些格式不尽相同,但它们都有一个共同的概念——段。

 段(segmentation)是指二进制文件内的区域,所有特定类型的信息都被保存在里面。size程序可查看文件的段信息。

 如:    size a.exe

====>   text  data  bss  dec  hex  filename

      2756  740  224  3720  e88  a.exe

  a.exe由正文段,数据段,bss段共计3720字节组成。其中正文段(Text Segment)存储指令,数据段(Data Segment)存储已初始化全局变量,BSS(BSS segment)存储未赋值全局变量。

  是不是觉得少了点什么东西呢?调用栈在哪里?调用栈并不存储在可执行文件中,而是在运行时动态创建,调用栈所在的段称之为 堆栈段 (Stack Segment),和其他段一样,他也有自己的大小,不能越界访问,否则将会出现段错误。无穷递归会不断的往调用栈里面加入栈帧,最后产生 栈溢出(Stack Overflow)。堆栈段在程序运行时动态创建,包含着调用栈,保存了函数调用关系和局部变量。

  栈空间的大小因操作系统而异,Linux中,栈大小由系统命令ulimit指定,例如:ulimit -a显示当前栈的大小,ulimit -s 32768将栈的大小设置为32MB。 在Windows中,栈的大小存储在可执行文件中,使用gcc可以这样指定可执行文件的栈大小:gcc -Wl,--stack=<栈大小>。实际上,栈大小是由连接程序ld指定的,编译参数-Wl的作用就是将其后stack参数传递给ld。

  以上,就可以理解为什么建议把较大的数组放在main函数的外面了:局部变量是放在堆栈段的。栈溢出不一定是函数调用层次太深,也可能是局部变量太大,只要是总大小超出了允许范围就会产生栈溢出。

  

转载于:https://www.cnblogs.com/iin111/p/3380280.html

你可能感兴趣的文章
Hive教程(1)
查看>>
第16周总结
查看>>
C#编程时应注意的性能处理
查看>>
Fragment
查看>>
比较安全的获取站点更目录
查看>>
苹果开发者账号那些事儿(二)
查看>>
使用C#交互快速生成代码!
查看>>
UVA11374 Airport Express
查看>>
P1373 小a和uim之大逃离 四维dp,维护差值
查看>>
NOIP2015 运输计划 树上差分+树剖
查看>>
P3950 部落冲突 树链剖分
查看>>
读书_2019年
查看>>
读书汇总贴
查看>>
微信小程序 movable-view组件应用:可拖动悬浮框_返回首页
查看>>
MPT树详解
查看>>
空间分析开源库GEOS
查看>>
RQNOJ八月赛
查看>>
前端各种mate积累
查看>>
jQuery 1.7 发布了
查看>>
Python(软件目录结构规范)
查看>>