编译简介

编译是把高级语言写的源文件翻译成二进制文件的过程。大家知道,机器只能识别1和0。最初的时候,程序是用二进制写的。后来有人发明了汇编语言,再后来有人发明了C/C++等高级语言。显然我们能够更容易理解和掌握更接近自然语言的高级语言。高级语言能够被用来更好的描述和解决问题。当然了,鱼和熊掌不可兼得。越高级的语言,往往要牺牲某些方面(比如效率)以改善易用性。有的时候还不得不适用低级语言,常见的情况是,时间敏感性非常高的场景。总之,没有万能的语言,各有所长。

         发展至今,机器还是只能认得0和1。高级语言写的程序必须经过编译才能变成可执行的二进制程序。

         分层是解决复杂问题的有效方法。编译也不例外。编译过程大致可分为如下步骤(以GCC为例):

1.        预处理

预处理器(cpp)将源文件a.c翻译成一个ASCII码的中间文件a.i。单独的编译命令如下:

cpp [otherarguments] a.c ./a.i

这一步主要完成的工作包括:扩展头文件、宏替换、条件编译。

扩展头文件:把包含的头文件的内容插入源码文件中,做为源码的一部分。

宏替换:例如有宏定义 #define PI  3.1415926

        在预处理阶段会把源文件中的PI全都替换为3.1415926

条件编译:删除不满足条件的程序段

                   例如a.c中有这样一段

                   #if0

                   inta, b;

                   a =b+3;

                   #endif

                   在预处理阶段,该段代码将被删除。

另外源码中的注释部分也将被删除。

2.        编译

C编译器(cc1)把a.i翻译成一个ASCII码的汇编语言文件a.s。单独的编译命令如下:

cc1  a.i a.c [other arguments] –o a.s

cc1是编译器的核心模块。

一个简单的.c文件:

void main()

{

              int a,b;

              a = b+3;

}

被翻译成如下的汇编语言文件

         .file  "main.i"

         .text

         .globl        main

         .type         main,@function

main:

.LFB1:

         .cfi_startproc

         pushl         %ebp

         .cfi_def_cfa_offset 8

         .cfi_offset 5, -8

         movl          %esp,%ebp

         .cfi_def_cfa_register 5

         subl  $16,%esp

         movl          -4(%ebp),%eax

         addl $3,%eax

         movl          %eax,-8(%ebp)

         leave

         .cfi_restore 5

         .cfi_def_cfa 4, 4

         ret

         .cfi_endproc

.LFE1:

         .size main,.-main

         .ident        "GCC:(Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"

         .section    .note.GNU-stack,"",@progbits

如此也可看出用汇编语言直接写程序是件多么低效的事情。

3.        汇编

汇编器(as)将a.s翻译成一个可重定位的目标文件a.o。单独的编译命令如下:

as [otherarguments] –o a.o a.s

在Linux上a.o是一个ELF文件。

 

4.        链接

链接器(ld)将相关的.o文件以及一些必要的系统目标文件组合起来,创建一个可执行的目标文件。

ld –o a.out[system objects files and args] a.o

链接器的主要任务是:

•   符号解析,目的是将每个符号引用刚好和一个符号定义联系起来。

•   重定位。把对符号的引用转换成存储器地址。

 

shell中输入a.out执行程序。shell调用一个叫做加载器的函数,它拷贝可执行文件a.out中的代码和数据到存储器,然后将控制代码转移到这个程序的开头。

 

目标文件

目标文件分为三种形式

▪  可重定位目标文件。也就是.o文件和.a文件(实际上是多个.o文件的集合)。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并,创建一个可执行目标文件。

▪  可执行文件。也就是可以直接拷贝到内存运行的文件。

▪  共享目标文件。即动态库。一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载到存储器并链接。

 

各个系统的目标文件格式是不相同的。所有不能把Linux的可执行程序放到Windows上面运行。Linux系统上的目标文件格式是ELF(Executableand Linkable Format)。目标文件除了包含可执行代码和数据外,好包含系统为执行程序需要的一些信息:生成该文件的系统的字的大小和字节顺序、目标文件的类型、机器类型等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值