首先看一段代码:
#include <stdio.h>
int glob_val; //global uninitialized
int glob_val2 = 2; //global initialized
//int glob_val5 = 4;
//int glob_val6 = 4;
static int glob_val3; //static global uninitialized
static int glob_val4 = 1; //static global initialized
int main(int argc , char *argv[]){
int auto_val ; //local uninitialized
int auto_val2 = 3; //local initialized
static int stat_val ; //static local uninitialized
static int stat_val2 = 4; //static local initialized
char *p = "Hello World!"; //point to constant
// register reg_val = 5 ; //register
printf( "global uninitialized var's addr:"
" %#08lx\n " , &glob_val );
printf( "global initialized var's addr:"
" %#08lx\n " , &glob_val2 );
printf( "global initialized var's addr:"
" %#08lx\n " , &glob_val3 );
printf( "static global initialized var's addr:"
" %#08lx\n " , &glob_val4 );
printf( "constant value's addr: %#08lx \n "
"pointer's addr: %#08lx\n" , p , &p );
// printf( "register uninitialized var's addr:"
// " %#x\n " , ®_val );
printf( "local uninitialized var's addr:"
" %#08lx\n " , &auto_val );
printf( "local initialized var's addr:"
" %#08lx\n " , &auto_val2 );
printf( "static local uninitialized var's addr:"
" %#08lx\n " , &stat_val );
printf( "static local initialized var's addr:"
" %#08lx\n " , &stat_val2 );
return 0;
}
输出的结果为:
然后我们再来看 APUE P164的配图global uninitialized var's addr: 0x601058
global initialized var's addr: 0x601040
global initialized var's addr: 0x601050
static global initialized var's addr: 0x601044
constant value's addr: 0x400698
pointer's addr: 0x7fff54c54688
local uninitialized var's addr: 0x7fff54c54680
local initialized var's addr: 0x7fff54c54684
static local uninitialized var's addr: 0x601054
static local initialized var's addr: 0x601048
从上面可以看出,内存大致分为几个段:
代码段(正文段):代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
BSS段:BSS段(bsssegment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
从上面程序运行的结果,可以看出:
1.函数代码存放在代码段。声明的类如果从未使用,则在编译时,会优化掉,其成员函数不占代码段空间。
全局变量或静态变量,放在数据段,注:按理来说 ,没有声明的全局变量或静态变量应该保存在BSS中,但结果看出感觉它们在一块
局部变量放在栈中,
用new产生的对象放在堆中,
常量保存在常量区,常量区地址比数据段地址更低
CPU寄存器:CPU寄存器,其实就是来控制代码段和数据段的指令及数据读取的地方,当然,CPU也有自己存放数据的地方,那就是通用寄存器里的数据寄存器,通常是EDX寄存器,C语言里有个register,就是把数据放在这个寄存器里,这样读取数据就相当的快了,因为不用去内存找,就省去了寻址和传送数据的时间开销。他还有一些寄存器是用来指示当前代码段的位置、数据段的位置、堆栈段的位置等等(注意这里存放的只是相应的代码或数据在内存中的地址,并不是实际的值,然后根据这个地址,通过地址总线和数据总线,去内存中获取相应的值),不然在执行代码的时候,指令和数据从哪取呢?呵呵。。。他里面还有标志寄存器,用来标识一些状态位,比如标识算术溢出呀等等。。。。。
寄存器是特殊形式的内存,嵌入到处理器内部。
每个进程需要访问内存中属于自身的区域,因此,可将内存划分成小的段,按需分发给进程。
寄存器用来存储和跟踪进程当前维护的段。偏移寄存器(Offset Registers)用来跟踪关键的数据放在段中的位置。在进程被载入内存中时,基本上被分裂成许多小的节(section)。我们比较关注的是6个主要的节:
(1) .text 节
.text 节基本上相当于二进制可执行文件的.text部分,它包含了完成程序任务的机器指令。
该节标记为只读,如果发生写操作,会造成segmentation fault。在进程最初被加载到内存中开始,该节的大小就被固定。
(2).data 节
.data节用来存储初始化过的变量,如:int a =0 ; 该节的大小在运行时固定的。
(3).bss 节
栈下节(below stack section ,即.bss)用来存储为初始化的变量,如:int a; 该节的大小在运行时固定的。
(4) 堆节
堆节(heap section)用来存储动态分配的变量,位置从内存的低地址向高地址增长。内存的分配和释放通过malloc() 和 free() 函数控制。
(5) 栈节
栈节(stack section)用来跟踪函数调用(可能是递归的),在大多数系统上从内存的高地址向低地址增长。
同时,栈这种增长方式,导致了缓冲区溢出的可能性。
(6)环境/参数节
环境/参数节(environment/arguments section)用来存储系统环境变量的一份复制文件,
进程在运行时可能需要。例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、主机名等信息。
该节是可写的,因此在格式串(format string)和缓冲区溢出(buffer overflow)攻击中都可以使用该节。
另外,命令行参数也保持在该区域中.
另外还有一篇文章:http://www.cppblog.com/prayer/archive/2009/08/17/93594.html我去验证BSS和数据段,没有验证成功。