《程序员的自我修炼》学习笔记(二)

1. 概述

构建(Build):预处理(Preprocess)、编译(Compilation)、汇编(Assembly)、链接(Linking)

本质:软件运行背后的机理及支撑软件运行的各种平台和工具

 

2. 编译过程分解

   

2.1 预编译

主要处理源代码文件中以“#”开始的预编译指令,如“#include”,“#define”

gcc -E hello.c -o hello.i

处理规则如下

1)将所有的“#define”删除,并且展开所有宏定义

2)处理所有条件预编译指令,如“#if”,“#ifdef”,“elif”,“#else”,“#endif”

3)处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置(该过程是递归的)

4)删除所有的注释“//”,“/**/”

5)添加行号和文件名标识,如#2 “hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或告警时显示行号

6)保留所有的#pragma编译器指令,因为编译器需要使用

 

2.2 编译

对预编译完的文件进行词法分析、语法分析、语义分析、优化,生成汇编代码文件

gcc -S hello.i -o hello.s

gcc -S hello.c -o hello.s

 

2.3 汇编

将汇编代码转换为机器可以执行的指令,每个汇编语句都对应一条机器指令。

as hello.s -o hello.o

gcc -c hello.s -o hello.o

gcc -c hello.c -o hello.o

 

2.4 链接

ld

 

3. 编译器

编译过程包含:扫描、语法分析、语义分析、源代码优化、代码生成、目标代码优化

 

3.1 词法分析(Scanner)

源代码程序输入到扫描器,将源代码的字符序列分割成一系列的记号。

记号分类:关键字、标识符、字面量(数字、字符串)、特殊符号。

将标识符存放到符号表,将数字、字符串常量存放到文字表。

 

3.2 语法分析(Grammer Parser)

对扫描器产生的记号进行语法分析,采用上下文无关语法,产生语法树(以表达式为节点的树)。

符号和数字是最小的表达式。

 

3.3 语义分析

语法分析只是完成对表达式的语法层面的分析,并不了解这个语句是否真正有意义。

编译器所能分析的语义是静态语义,即在编译期可以确定的语义。

静态语义包含声明和类型的匹配、类型的转换。

经过语义分析后,语法树上的表达式都被标识了类型。

 

3.4 中间语言生成

源码级优化器将整个语法树转换为中间代码,一般跟目标机器和运行时环境无关。

中间代码采用三地址码或P-代码。

中间代码将编译器分为前端和后端,前端负责产生机器无关的中间代码,后端将中间代码转换为目标机器代码。

针对跨平台的编译器,可以采用同一个前端和针对不同机器平台的多个后端。

 

3.5 目标代码优化

编译器后端包含代码生成器和目标代码优化器。

代码生成器将中间代码转换为目标机器代码,该过程依赖目标机器,因为不同机器的字长、寄存器、整数数据类型和浮点数据类型不同。

目标代码优化器对目标代码进行优化

 

4. 链接器

重定位:重新计算各个目标地址的过程

符号:表示一个地址

C:最小的单位是变量和函数;若干个变量和函数组成模块;

Java:每个类是一个模块;若干个类模块组成包;若干个包组成程序;

链接:把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确地衔接。包括地址和空间分配符号决议重定位

最常见的库:运行时库

 

posted @ 2022-03-14 00:47  褴褛披风  阅读(55)  评论(0)    收藏  举报