C程序的内存布局
1.代码段(code或text):
通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。 某些架构也允许代码段为可写,即允许修改程序。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等,但一般都是放在只读数据段中。
2.数据段(data segment):
通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。
数据段属于静态内存分配,可以分为只读数据段和读写数据段。
字符串常量等,但一般都是放在只读数据段中。
3.BSS段(bss):
通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。
BSS节不包含任何数据,只是简单的维护开始和结束的地址,即总大小,以便内存区能在运行时分配并被有效地清零。
BSS节在应用程序的二进制映象文件中并不存在,即不占用磁盘空间而只在运行的时候占用内存空间,
所以如果全局变量和静态变量未初始化那么其可执行文件要小很多。BSS段属于静态内存分配。
这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?
变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,
那就是不管什么类型都初始化为0,这种没有显示初始化的就是我们这里所说的未初始化。
既然都是0那么就没必要把每个0都存储起来,从而节省磁盘空间,这是BSS的主要作用。
4.堆(heap):
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
5.栈(stack):
栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
由于栈的后进先出特点,所以栈特别方便用来保存/恢复调用现场。
从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
简单示例如下:
const char ro[ ] = {"this is read onlydata"}; //只读数据区 static char rw_1[ ] ={"this is globalread write data"}; //已初始化读写数据段 char BSS_1[ 100]; //未初始化数据段 const char *ptrconst ="constantdata"; //字符串放在只读取数据段 int main() { short b; //在栈上,占用2个字节 char a[100]; //在栈上开辟100个字节,工的值是其首地址 char s[ ]="abcdefg"; //s在栈上,占用4个字节 //"abcdefg"本身放置在只读数据存储区,占8个字节 char *p1; //p1在栈上,占用4个字节 char *p2="123456"; //p2 在栈上,p2指向的内容不能改, //“123456”在只读数据区 static char rw_2[ ]={"this is local read write data"}; //局部已初始化读写数据段 static char BSS_2[100]; //局部未初始化数据段 static int c = 0; //全局(静态)初始化区 p1=(char *)malloc(10 * sizeof(char ) ); //分配内存区域在堆区 strcpy(p1,"xxxx"); //“XXXX”放在只读数据区,占5个字节 free(p1); //使用free释放p1所指向的内存 return 0; }
我们再看一下以下的代码:
#include <stdio.h> const int a = 10; ////全部常量a int main() { const int b = 20; //局部常量b int* pa = (int*)&a;//报错,因为全部常量放在只读数据段 int* pb = (int*)&b;//修改成功,因为局部常量放在栈上 *pa = 30; *pb = 30; return 0; }