2 编译和链接

2.1被隐藏了的过程

2 编译和链接

gcc hello. c
./a.out

  • 上述分4步
  • Prepressing、
  • Compilation、
  • Assembly
  • 链接( Linking),如图2-1
2 编译和链接

2.1.1预编译

  • hello.c和相关的头文件,等被预编译器cpp预编译成一个.i
  • C++源代码文件的扩展名可能.cpp或.cxx
    • 头文件扩展名可能.hpp,
    • 预编译后扩展名是.ii
  • 预编译的过程相当于(E表示只预编译)
2 编译和链接

  • “#define”删除,展开宏定义
  • 处理所有条件预编译指令,
    • “#if”、“#ifdef’”、“#elif”、“#else"”、“# endif”
  • 处理“#include”
    • 被包含的文件插入到该预编译指令的位置。
    • 这个过程是递归,
  • 删除注释
  • 添加行号和文件名标识
    • #2“ hello.c” 2,
    • 便于编译时编译器产生调试用的行号信息
    • 用于編译时产生编译错误或警告时能够显示行号
  • 保留所有的#pragma,编译器须要
  • 预编译后的i文件不含任何宏定义
    • 且包含的文件也已经被插入到i文件
    • 所以当我们无法判断宏定义是否正确或头文件包含是否正确时,
    • 可查看预编译后的文件

2.1.2编译

  • 编译
    • 预处理完的文件词法、语法、语义及优化后生产相应的汇编代码
  • 相当于
  • gcc -S hello.i -o hello.s
  • 现版本GCC把预编译和编译两步合并一个
  • cc1位于“/usr/lib/gcc/i486-linux-gnu/4.1/cc1”
  • 直接调ccl完成
2 编译和链接
  • 都可得到汇编输出文件hello.S
  • 对C,预编译和编译的程序是cc1
  • 对C++,有对应的程序叫做 cc1plus
  • Objective-C是 cclobj:
  • fortran是f771
  • Java是jcl
  • gcc命令只是这些后台程序的包装,
    • 它根据不同的参数要求去调用预编译编译程序cc1
    • 汇编器as、链接器ld

2.2编译器了什么

  • 用C/C++写的程序用编译器将其翻译成机器可执行的指令及数据
  • 用机器语言或汇编语言编写的程序依赖于特定的机器,
    • 为某种CPU编写的程序在另外一种CPU下完全无法运行,需要重新编写
  • 期望能用类似于自然语言的语言来描述一个程序,
    • 但自然语言的形式不够精确,
    • 类似于数学定义的编程语言很快就诞生
  • 六七十年代诞生高级语言
    • 如 FORTRAN、C(FORTRAN诞生50年代的IBM)。
  • 高级语言使得程序员们能够更加关注程序逻辑的本身,
    • 少考虑计算机本身的限制,如字长、内存大小、通信方式、存储方式
  • 高级语言可移植性使得它在多种计算机平台下能够游刃。
  • 高级语言的开发效率是汇编语言和机器语言的5倍以上

  • 编译过程一般可以分为6步:打描、语法分析
  • 语义分析、源代码优化、代码生成和目标代码优化。
2 编译和链接

2 编译和链接

2.2.1词法分析

  • 源代码被输入到扫描器,扫描器任务简单,简单进行词法分析
  • 用一种类似于有限状态机的算法可以很轻松地将源代码的字符序列分割成一系列的记号。
  • 28个非空字符,经过扫描以后,产生了16个记号,如表2-1
2 编译和链接

2.3链接器年岭比编译器长

  • 很久前
  • 将源代码都写在同一文件
  • 后来程序源代码文件百万行

  • 为更好理解编译和链接
  • 并非从一开始就这么复杂的自动化编译、链接过程
  • 链接概念远在高级程序语言之前就已存在
    • 先把一个程序在纸上写好,当时没有高级语言,
    • 连汇编都没
  • 程序要被运行时,人工将程序写入到存储设备上,
    • 最原始的存储设备之一就是纸帯,即在纸带上打相应的孔。

  • 第一条指令目的地址是第5条指令
    • 纸带每行有8孔,穿孔表0,未穿表1

2 编译和链接

  • 第1条指令之后、第5条指令前插多条指令
    • 第5条指令及后面的指令的位置将会往后移动
    • 第一条指令的低4位将要调整
  • 重新计算每个子程序或跳转的目标地址
    • 这些位置都要重新计算,繁琐耗时,易出错。
  • 重新计算各个目标的地址过程叫重定位

  • 有多条纸带,
    • 程序之间可能会有类似的跨纸带之间的跳转,
    • 经常修改导致跳转目标地址变化在程序拥有多个模块的时候更严重
  • 人工绑定进行指令的修正以确保所有的跳转目标地址都正确,
    • 程序规模大以后变得复杂

2.4模块拼装一静密链接

  • 人们把每个源代码模块独立编译
  • 按照须要将它们“组装”,组裝模块就是链接( Linking)。
  • 链接就是把各个模块之间相互引用的部分都处理好,使各模块间正确衍接
  • 链接器所要做的工作其实跟
    • “程序员人工调整地址”本质上没两样,
    • 现代高级语言的诸多特性和功能,
    • 使编译器、链接器更为复杂,功能更强大,
    • 原理上它的工作就是把一些指令对其他符号地址的引用修正
  • 链接过程包括地址和空间分配、符号决议( Symbol Resolution)
    • 重定位( Relocation)等

2.5本章小结