数据和代码
编程语言理论经典对立之一就是代码和数据的区别,有些语言如LISP把两者视为一体,其他语言如C语言则维持两者的区别。编译绝大部分工作都跟翻译代码有关,必要的数据存储管理的绝不部分都在运行时进行。
学习运行时可以有三个好处,有助于优化代码,获得最佳效率,有助于理解更高级的材料,陷入麻烦的时候,可以更容易的分析问题。
段(Segments)
ELF:(原意为Extensibla Linker Format,可扩展链接器格式,现在代表Executable and Linking Format,可执行文件和链接格式),存在于绝大多SVr4实现。
COFF:(Common Ojbect-File Format,普通目标文件格式),存在于其他系统中。
上述两种不同的格式有一个共同的概念,段,段的概念和很多都相关,单就目标文件而言,段就是指二进制文件中简单的区域,里面保存了和某种特定类型(如符号表条目)相关的信息。术语Section也被广泛使用,section是ELF文件中的最小组织单位,一个段一般包含几个section。
上述段的概念是UNIX中的,和Intel X86架构的段完全没有关系。UNIX中段就表示一个二进制文件的内容块,intel X86中段表示一种设计的结果,在这种设计中,地址空间并非一个整体,而是分成64k大小的区域,称之为段,这种设计也是情非得已,为了向下兼容曾经的芯片。
a.out的结构
下图显示了编译器和连接器分别在这些段中写入了什么东西
BSS段是“Block Started by Symbol(由符号开始的块)”的缩写,它是旧式IBM704汇编程序的一个伪指令,UNIX借用了这个名字,至今仍然沿用。有些人喜欢把它记作“Better Save Space(更有效的节省空间)”,由于BSS段只保存没有值得变量,所以事实上并不需要保存这些变量的映象。运行时所需要的BSS段的大小记录在目标文件中,但BSS段不像其他段,它并不占用目标文件的任何空间
可以使用size命令和nm程序来查看程序各个部分的大小
操作系统之于a.out
为什么要用段来组织,因为段可以方便的映射到链接器,在运行时可以直接载入,载入器只需要提取文件中每个段的映像。段在正在执行的程序中是一块内存区域,每个区域有特定的目的。
文本段包括程序的指令
数据段包括经过初始化的全局变量和静态变量以及它们的值,BSS段的大小从可执行文件中得到,然后链接得到这个大小的内存块,紧跟数据块之后,这块内存进入程序后全部清零。
C语言运行时系统之于a.out
运行时数据结构包括,堆栈、活动记录、数据和堆等
- 堆栈段:主要有三个用途,和函数以及表达式有关。除了递归调用,堆栈并不是必须的。在大多数CPU中,堆栈是向下增长的,也就是朝着低地址方向生长。
- 为函数内部的局部变量提供存储空间
- 函数调用过程中,保持一些维护性信息,称为堆栈结构或者过程活动记录
- 暂存区,很长的算数表达式
和C++不同,C运行时函数个个短小精悍,使得C很高效
C语言不允许在函数中定义函数,也就是函数不能嵌套定义。
setjmp和longjmp
以上两个命令是C语言独有的,通过操纵过程活动记录实现的。
- setjmp(jmp_buf j)必须首先被调用。它表示使用变量j记录现在的位置,函数返回0;
- longjmp(jmp_buf j,int i)可以接着被调用,它表示回到j所记录的位置,让他看上去像刚从setjmp函数返回一样。但是函数返回i,使代码知道它实际上是通过longjmp返回的。
- 当使用longjmp时,j的内容被销毁
- setjmp保存了一份程序计数器和当前的栈顶指针,也可以保存一些初始值,longjmp恢复这些初始值。
- 和goto不同,第一,goto不能跳出C语言的函数;第二,longjmp只能回到曾经到过的地方。
- 需要注意的是,保证局部变量在longjmp过程中一直保持它的值得唯一可靠办法就是把它声明为volatile。
- 两者组合最大用途是错误恢复,只要还没从函数返回,一旦发现一个不可恢复的错误,就可以把控制流转移到主输入循环。可以用它来从一串很深的代码中立即返回,提防潜在的危险代码。目前C++已经支持了异常系统
switch(setjmp(jbuf))
{
case 0:
apple = *suspicious;
break;
case 1:
printf("suspicious is a bad pointer\n");
break;
default:
die("unexpected value returned bt setjmp");
}
- 和goto一样,不是必要的情况下不要使用它们。
UNIX和MS-DOS的堆栈段
- 在UNIX中,当进程需要更多空间的时候,堆栈会自动生长。
- DOS中,在建立可执行文件时,堆栈大小必须同时确定,而且它不能在运行时增长。Stack overflow是常见的堆栈溢出错误。
有用的C语言工具
Reference
C专家编程