揭秘编译和链接--C++

《程序员的自我修养--链接、装载与库》学习笔记

对应平常的应用开发,我们很少关注编译和链接过程,这些事都被IDE替我们完成了。那我们写的代码,是怎么组织起来的?最后是怎么运行起来的呢?今天跟大家一起来学习下。

代码编写完成到最后变成可执行的exe,中间需要四个步骤:预编译、编译、汇编和链接

          

 

预编译

定义:由源文件“.cpp/.c”生成“.i”文件,这是在预编译阶段完成的;gcc -E .cpp/.c --->.i

功能:

  •  展开所有的宏定义,消除“#define”;
  •  处理所有的预编译指令,比如#if、#ifdef等;
  •  处理#include预编译指令,将包含文件插入到该预编译的位置;
  •  删除所有的注释“/**/”、"//"等;
  •  添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及错误提醒;
  •  保留所有的#program编译指令,原因是编译器要使用它们;

编译

定义:由“.i”文件生成“.s”文件,生成汇编代码文件,这是在编译阶段完成的;gcc -S .i --->.s

功能:

  • 词法分析:将源代码文件的字符序列划分为一系列的记号,一般词法分析产生的记号有:标识符、关键字、数字、字符串、特殊符号(加号、等号);在识别记号的同时也将标识符放好符号表、将数字、字符放入到文字表等;有一个lex程序可以实现词法扫描,会按照之前定义好的词法规则将输入的字符串分割成记号,所以编译器不需要独立的词法扫描器;
  • 语法分析:语法分析器将对产生的记号进行语法分析,产生语法树----就是以表达式尾节点的树,一步步判断如何执行表达式操作。下图为一个语法树
  • 语义分析:由语法阶段完成分析的并没有赋予表达式或者其他实际的意义,比如乘法、加法、减法,必须经过语义阶段才能赋予其真正的意义;
  • 字符集映射
  • 逻辑翻译
  • 优化后生成相应的汇编代码文件
  • 汇总所有符号

汇编

定义:由“.s”文件生成的“.obj”文件,生成可重定位的二进制文件;gcc -c .s-->.o;

功能:

  • 此文件中生成符号表,能够产生符号的有:所有数据都要产生符号、指令只产生一个符号(函数名);
  • 一个cpp生成一个obj
  • 数据段、代码段
  • 导出符号表
  • 未解决符号表
  • 地址重定向表

链接

定义:链接得到库(dll)或可执行文件(exe)

功能:

  • 合并所有“.obj”文件的段
  • 调整段偏移和段长度(按照段的属性合并,属性可以是“可读可写”、“只读”、“可读可执行”,合并后将相同属性的组织在一个页面内,比较节省空间)
  • 合并符号表,进行符号解析完成后给符号分配地址;其中符号解析的意思是:所有.obj符号表中对符号引用的地方都要找到该符号定义的地方。在编译阶段,有数据的地方都是0地址,有函数的额地方都是下一行指令的偏移量-4(由于指针是4字节);
  • 可执行文件以页面对齐。
  • 符号的重定位(链接核心):将符号分配的虚拟地址写回原先未分配正确地址的地方

编译和链接图示