text段、data段、bss段、堆和栈

来源:互联网 发布:安邦 知乎 编辑:程序博客网 时间:2024/05/19 20:19

text段

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

文字常量区

文字常量区:常量字符串、全局常量与静态常量存放在文字常量区(局部非静态常量存放在栈),此处存放的数据不可修改,程序结束后由系统释放。注意,若全局常量与静态常量被volatile关键字修饰,此时存放位置可能在文字常量区也可能不在,跟编译器有关。

bss段

bss段:bss段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域,bss段并不给该段的数据分配空间,只是记录数据所需空间的大小,具体体现为一个占位符,所以,bss段是不占用exe文件空间的,其内容由操作系统初始化(清零)。bss是英文Block Started by Symbol的简称。bss段属于静态内存分配。

data段

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域,数据保存在目标文件中。数据段属于静态内存分配。bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含data段和bss段的整个区段通常称为数据区。

堆(heap)

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减),若程序员不释放,程序结束时可能由系统回收。

堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

栈(stack)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。它是由操作系统分配的,内存的申请与回收都由系统管理。

在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在Windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

示例

#include <iostream>using namespace std;int a = 0;  // data段int b;  // bss段int c = a;  // bss段const int d = 0;  // 文字常量区int e = d;  // data段volatile const int f = 0;  // 可能在文字常量区,也可能不在,跟编译器有关int main() {    static int g = 0;  // data段    static int h;  // bss段    static const int i = 0;  // 文字常量区    volatile static const int j = 0;  // 可能在文字常量区,也可能不在,跟编译器有关    int k = 0;  // 变量i在栈,字面值0在text段    const int m = 0;  // 常量j在栈,字面值0在text段    char *pa = "pa";  //  指针pa在栈,字符串在文字常量区    char pb[] = "pb";  // 数组pb在栈,字符串在text段    char *pc = new char[3]{'p', 'c', '\0'};  // 指针pc在栈,指针所指向的空间在堆    delete[]pc;  // 释放内存    return 0;}

补充1:静态局部变量在什么时候被初始化?

我们已经知道,已初始化的静态局部变量,其初始值保存在可执行文件中,内核在启动该程序时从源程序文件读入。换句话说,已初始化的普通局部变量,其初始值保存在text段,当程序运行到此处时,对该局部变量赋初始值,但已初始化的静态局部变量却并不是这样,当初始值为常量时,静态局部变量保存在data段,早在程序加载时,系统就已经对其赋初始值,而不是等到程序运行到我们给其赋初始值的时候才赋值;当初始值为变量时,静态局部变量保存在bss段,在程序加载时,系统就对其赋默认值0,静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。

在下面的测试代码中,static int b = 0xb;并没有生成相对应的汇编代码,即,初始值不是保存在text段,程序也不执行该语句,从测试结果我们也可以看到,在我们给变量b赋初值之前,该变量就已经被赋值为0xb了。

测试代码:

#include <iostream>using namespace std;int a = 0xa;int main() {    int *p = &a;    cout<<p<<":"<<*p<<endl;    p++;    cout<<p<<":"<<*p<<endl;    static int b = 0xb;    cout<<&b<<":"<<b<<endl;    return 0;}

输出结果:

0x404004:100x404008:110x404008:11

补充2:为什么栈的速度比堆要快?

堆和栈哪个更快,从两方面来考虑:

  1. 分配和释放。堆在分配和释放时都要调用函数malloc,free,比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,具体可以看看mallocfree的源代码,他们做了很多额外的工作,而栈却不需要这些。栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。

  2. 访问时间。访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次。另外,堆的内容被操作系统交换到外存的概率比栈大,栈一般是不会被交换出去的。

综上所述,站在操作系统以上的层面来看,栈的效率比堆高。

参考链接

数据区、代码区、栈区、堆区
数据段、代码段、堆栈段、BSS段的区别
.bss段和.data段的区别
text段,data段,bss段,堆和栈
为什么栈的速度比堆要快