C中变量存储区、程序编译后的映像和运行时的段分析

1.栈 --由编译器自动分配释放,如局部变量。
2. 堆 --般由程序员分配释放,由程序员释放,如c中的malloc和free函数,和c++中的new和delete操作符。
3. 全局区(静态区) --全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束释放。
4.常量区。字符串常量放与此处,程序结束自动释放

5.程序代码区 存放程序的二进制代码

 

代码:
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);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456");
//123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块。
}

 

程序运行时内存的大致分布

+------------------+ 内存低端

| 代码段 |

|------------------|

| 数据段 |

|------------------|

| 堆  栈  |

+------------------+ 内存高端

这些区(数据结构)在编译之后的映像和运行时的两种结构

编程语言的的经典对立之一就是代码和数据的区别。代码和数据的区别也可认为是运行时和编译时的界限,编译器的绝大部分工作和翻译代码有关;必要的数据存储管理的绝大部分都在运行时进行。

编译之后的映像

下面以GCC编译器得到的a.out文件展开说明:

a.out:assembler output(汇编程序输出),但是,他不是汇编程序输出,而是链接器输出,这个名字曾被解释为:“新程序就绪,准备执行”它是链接器输出文件,它是一种二进制文件,存放的二进制是有规律的。

编译生成的目标文件和链接生成的可执行文件(本人的相关博文)都有不同的格式,但是它们都由段(segment)构成,段是二进制文件的简单区域,存放某种特定类型相关的所有信息。

段的感性认识,引入汇编程序:

 汇编系统预定义的段名

l         .text     @代码段

l          .data   @初始化数据段 .data Read-write initialized longdata.

l         .bss     @未初始化数据段

l          .sdata  @ .sdata Read-write initialized short data.

l         .sbss    @

 

简化段伪指令

功 能

注释

.CODE [段名]

创建一个代码段

段名为可选项,如不给出段名,则采用默认段名。对于多个代码段的模型,则应为每个代码段指定段名。

.DATA

创建一个数据段

段名是:_DATA

.DATA?

创建无初值变量的数据段

段名是:_BSS

.FARDATA [段名]

建立有初值的远调用数据段

可指定段名,如不指定,则将以FAR_DATA命名。

.FARDATA? [段名]

建立无初值的远调用数据段

可指定段名,如不指定,则将以FAR_BSS命名。

.CONST

建立只读的常量数据段

段名是:CONST

.STACK [大小]

创建一个堆栈段并指定堆栈段大小

段名是:stack。如不指定堆栈段大小,则缺省值为1KB


接着谈a.out二进制文件的分布,下面是其示意图:

 

a.out magic number:比如宏定义,#defineMAX_NUM 100

文本段(The text segment )包含程序的指令,链接器把指令直接从文件拷贝到内存中,以后就用管他,因为一般情况下下,文本区域是不会改变的,不论是大小还是内容。

数据段(The data segment)包含经过初始化的全局变量和静态变量以及他们的值。BSS段存放全局或静态的尚未初始化的数据变量,大小可从可执行文件中得到,然后链接器得到这个大小的内存块。包含数据段和BSS段的一般统称为数据区。这是因为,操作系统中,段是一块连续地址,所以相邻的段被结合。一般来讲,数据段在任何进程中都是最大的段。

堆栈段(The stack segment)上图显示了一个即将执行的进程的内存布局,我们仍然需要一些存储空间,用于存放局部变量,临时数据,传递到函数中的参数等等(local variables, temporaries, parameter passing in function calls,)。

Unmapped:位于虚拟地址空间的最低部分,它未被映射到程序中有用的部分,访问它是非法的,因为即使它位于地址空间内,但未给它分配真实的物理地址空间。

 

如果程序中还使用了共享库,进程的地址空间:

 

上面是编译时,C运行时对a.out的操作 

运行时阶段,下面主要讲堆栈段的变化

The Stack Segment 堆栈段:

堆栈段包含一种单一的数据结构:堆栈。

堆栈为函数内部声明的局部变量提供存储空间。

函数调用的时候,堆栈存储相关的一些维护信息。这些信息被称为堆栈结构(stack frame)也叫做过程活动记录(precedure activation record)稍后讨论。

堆栈也可以作为临时存储区,有时候进程需要一些临时存储空间,比如执行一个复杂的计算时,可以把结果压到堆栈中。

值得一提的是:除了递归调用之外,堆栈并非必须。

以函数的调用为例:


c运行时系统在他自己的地址空间内如何管理程序的呢?这里做一个简单的讨论。

c语言自动提供一种用于函数调用的功能:称作调用链( keepingtrack of the call chain)记录了哪些函数调用哪些函数,以及return执行后,控制将返回什么地方。

解决这个问题的经典机制就是堆栈中的过程活动记录,每一个函数调用都会产生一个过程记录。其实它就是一种数据结构,记录调用后返回调用点需要的全部信息。

如下图就是一个过程活动记录的结构,不同的编译器会有所差别,但目的都是记录调用后返回调用点的信息。

下面展示递归调用过程:

C语言在编译和连接后,将生成代码段(Text)、只读数据段(RO Data)和读写数据段(RW Data)。在运行时,除了以上三个区域外,还包括未初始化数据段(BSS)区域和堆(Heap)区域和栈(Stack)区域。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值