读书笔记之c专家

第6章:运行时的数据结构

BSS段是“Block Started by Symbol(由符号开始的块)”。BSS只保存没有值的变量,运行时所需要的BSS段的大小

记录在目标文件中,但BSS并不占据目标文件的任何空间。

c程序的存储空间布局:


说明:

正文段:CPU执行的机器指令部份,通常是正文段可以共享的且通常是只读的,以防止程序由于意外而修改其自身指令。

初始化数据段:通常被称为数据段,包含了程序中需明确地赋初值的变量。

非初始化数据段(bss block started by symbol):程序开始前,内核将此段中的数据初始化为0或空指针。如以下声明:long  sum[100];将会被放于非初始化数据段。

栈:自动变量,每次函数调用时所需要保存的信息

堆:动态分配内存


1:编译hello word程序:gcc -pg hello.c; size a.out

 text   data    bss   dec   hex   filename
 1302    280     12   1594  63a  a.out
2:增加1个全局的int[1000]数组声明,重新编译

text   data    bss       dec      hex  filename
1302    280   4032   5614   15ee  a.out
bss的值变化表明bss段保存没有值的变量
3:在数组声明中增加初始值:

   text      data    bss   dec   hex  filename
   1302      4304    12   5618  15f2 a.out

如果数组是初始化的 则保存在data段儿,data 段保存初始化后全局和静态变量

4:函数内声明数组:mian内 int a[3000];

   text	   data	    bss	    dec	    hex	filename
   1318	    280	     12	   1610	    64a	a.out

main内定义数组,保存在栈里

5:在函数内声明数组并初始化:

   text	   data	    bss	    dec	    hex	filename
   1915	    284	     12	   2211	    8a3	a.out

通过以上测试是自己确信

*数据段保存在目标文件中

*BBS段不保存在目标文件中(除了记录BSS段在运行时的大小)

*文本段是最容易受优化措施影响的段

*a.out文件的大小受调试状态下编译的影响,但段不受影响

图片转自: http://blog.chinaunix.net/u2/87570/showart_2120069.html

上图:C语言的各部分会出现在哪些段中

------------------------------------------------------------------------------------------

6.4 操作系统在a.out里干了什么 

上图:可执行文件中段在内存中如何布局

上图显示了一个即将执行的程序的内存布局,仍需要一些内存空间,来保存局部变量,临时数据,函数参数等,这就是堆栈段,还需要堆(heap)空间,用于动态的分配内存(调用malloc函数)

文本段包含程序的指令,连接器把指令直接从文件拷贝到内存中(一般使用mmap系统调用),以后便不再管它,因为在典型情况下,程序的大小和内容都不会改变

包含数据段和BSS段的整个区段通常统称为数据区,操作系统术语里段是一片连续的虚拟地址,一般情况下,在任何进程中数据段是最大的段。

------------------------------------------------------------------------------------------------

6.4 c语言在运行时系统在a.out里干了什么

堆栈段有三个主要的用途,其中两个跟函数有关,另一个跟表达式计算有关。
  1.堆栈为函数内部声明的局部变量提供存储空间。
  2.进行函数调用时,堆栈存储于此有关的一些维护性信息。这些信息被称为堆栈结构(stack frame,有的书中译为栈帧),另外一个更常用的名字是过程活动记录(precedure activation recored).它包括函数调用地址(即当所调用的函数结束后跳回的地址)、任何不适合装入寄存器的参数以及一些寄存器值的保存。
  3.堆栈也可以作为暂时存储区,例如:计算很长的函数表达式时,通过alloca()函数分配的内存位于堆栈中(它会被下一个函数调用覆盖)

6.5 当函数被调用时发生了什么:过程活动记录

C语言自动提供的服务之一就是跟踪调用链——哪些函数调用了哪些函数,当下一个“return”语句执行后,控制将返回何处等。解决这个问题的经典机制是堆栈中的过程活动记录。当每个函数被调用时,都会产生一个过程活动记录(或类似的结构)。过程活动记录是一种数据结构,用于支持过程调用,并记录调用结束以后返回调用点所需要的全部信息。


结构的具体细节在不同的编译器中各不相同,这些字段的次序可能很不相同,而且可能还存在一个在调用函数前保存寄存器值的区域。
运行时系统维护一个指针(常常位于寄存器中),通常称为fp,用于提示活动堆栈结构。它的值是罪靠近堆栈顶部的过程活动记录的地址。


上图:每个函数调用在运行时创建的过程活动记录

6.6 auto和static关键字

char * favorite_fruit(){
   // static char deciduous[]="apple";

   char deciduous[]="apple";

 return deciduous;

 }

当进入该函数时,自动变量deciduous在堆栈中分配。当函数结束时,变量不复存在,它所占用的堆栈空间被回收,可能在任何时候被覆盖。如果想返回一个指向在函数内部定义的变量的指针时,要把那个变量声明为static。这样就能保证该数据保存在数据段中而不是堆栈中。该变量的声明期和程序一样长。

存储类型说明符auto关键字在实际中从来用不着。它通常由编译器设计者使用,用于标记符号表的条目——它表示“在进入该块后,自动分配存储”(与编译时静态分配或在堆上动态分配不同)。对于其他程序员来说,auto关键字几乎没有用处,因为它只能用于函数内部。但是在函数内部声明的数据缺省就是这种分配。

6.7控制线程

6.8setjmp和longjmp


posted @ 2012-03-22 08:49  foreverlearn  阅读(173)  评论(0编辑  收藏  举报