《读书笔记》程序员的自我修养之编译和链接
2016-03-15 20:24
731 查看
[b]《读书笔记》程序员的自我修养之编译和链接[/b]
对于经典的Hello world,程序是如何运行的呢?
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
对于GCC编译器,程序运行分为以下四个过程:
预编译(Prepressing)--à编译(Compilation)--à汇编(Assembly)--à链接(Linking)
$gcc –E hello.c –o hello.i
或
$cpp hello.c > hello.i
注:‘-E’选项表示只进行预编译;cpp是预编译器
预编译过程主要处理源代码文件中的以“#”开始的预编译指令(“#include”,“#define”等)。主要处理规则如下:
把所有的宏展开,删除所有的“#define”;
处理所有的条件编译(“#if”,“ifdef”,“#elif”,“#else”,“#endif”);
处理“#include”预编译指令,将被包含的文件插入到该预编译指令位置;
删除所有注释“//”和“/* */”;
添加行号和文件名标识(这就是为什么程序报错时会显示错误代码行号);
保留所有编译命令#pragma(留给编译器使用)。
编译过程命令如下:
$gcc –S hello.i –o hello.s
目前的GCC版本将预编译和编译过程合二为一,使用一个叫做ccl的程序来完成这两个过程。
可直接调用ccl来完成预编译和编译过程,如下:
$ /user/lib/gcc/i486–linux–gnu/4.1/ccl hello.c
或者
$gcc –S hello.c –o hello.s
汇编过程如下:
$as hello.s –o hello.o
或者
$gcc –c hello.s –o hello.o
或者
$gcc –c hello.c –o hello.o
链接过程是一个复杂的过程,相当复杂!!!
$ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o
看了上面的命令,为什么要将一大堆的文件链接起来才可以得到可执行文件??
且看后面的静态链接与动态链接篇幅。
上面分析了一段程序的执行过程,下面具体看看编译器到底做了什么工作。
编译过程一般可以分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。
1、扫描器(Scanner)的任务就是进行简单的词法分析,运用一种类似于有限状态机(Finite State Machine)的算法将源代码的字符序列分割成一系列的记号(Token)。词法分析产生的记号一般有以下几类:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)。扫描的过程由lex程序完成。
2、语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树(Syntax Tree)。简单来讲,由语法分析器生成的语法树就是以表达式(Expression)为节点的树。语法分析的过程由yacc工具完成。
3、语义分析由语义分析器(Semantic Analyzer)来完成。
----编译器所能分析的语义是静态语义,与之对应的动态语义只有在运行期才能确定。
----静态语义通常包括声明和类型的匹配,类型的转换。
----经过语义分析后,语法分析生成的语法树的表达式都被标识了类型,如果有些类型需要隐式转换,语义分析程序会在语法树中插入相应的转换节点。
4、源代码优化器对源代码进行优化,但直接在语法树上作优化比较困难,往往是将整个语法树转换成中间代码进行优化。
中间代码使得编译器分为前端和后端:前端负责产生机器无关的中间代码;后端负责将中间代码转换成目标机器代码。
5、目标代码优化器将上述生成的目标机器代码进行优化,比如选择合适的寻址方式、使用位移代替乘法运算、删除多余的指令等。
编译器忙活了半天,可生成的目标代码中,我们还不知道函数访问所要的目标函数的地址,变量访问所要的目标变量的地址,这可咋办呢??
其实目标函数访问也好,变量访问也好,这都可以归结为一种方式,就是所谓的模块间符号的引用。
人们把每个源代码模块独立地编译,然后按照需要将它们组装起来,这个组装模块的过程就是“链接”。
链接的主要内容就是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确衔接。
链接过程主要包括地址和空间分配、符号决议和重定位。
对于经典的Hello world,程序是如何运行的呢?
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
对于GCC编译器,程序运行分为以下四个过程:
预编译(Prepressing)--à编译(Compilation)--à汇编(Assembly)--à链接(Linking)
预编译
预编译是将.C文件预编译成.i文件$gcc –E hello.c –o hello.i
或
$cpp hello.c > hello.i
注:‘-E’选项表示只进行预编译;cpp是预编译器
预编译过程主要处理源代码文件中的以“#”开始的预编译指令(“#include”,“#define”等)。主要处理规则如下:
把所有的宏展开,删除所有的“#define”;
处理所有的条件编译(“#if”,“ifdef”,“#elif”,“#else”,“#endif”);
处理“#include”预编译指令,将被包含的文件插入到该预编译指令位置;
删除所有注释“//”和“/* */”;
添加行号和文件名标识(这就是为什么程序报错时会显示错误代码行号);
保留所有编译命令#pragma(留给编译器使用)。
编译
编译是整个过程的核心,也是最复杂部分之一。包括词法分析、语法分析、语义分析、源代码优化,生成汇编代码。编译过程命令如下:
$gcc –S hello.i –o hello.s
目前的GCC版本将预编译和编译过程合二为一,使用一个叫做ccl的程序来完成这两个过程。
可直接调用ccl来完成预编译和编译过程,如下:
$ /user/lib/gcc/i486–linux–gnu/4.1/ccl hello.c
或者
$gcc –S hello.c –o hello.s
汇编
汇编器(as)是将汇编代码转变为机器可执行的指令(汇编指令和机器指令的对照表一一翻译)汇编过程如下:
$as hello.s –o hello.o
或者
$gcc –c hello.s –o hello.o
或者
$gcc –c hello.c –o hello.o
链接
链接的过程是链接器(ld)将目标文件(.o文件)变为可执行文件(.exe)的过程。链接过程是一个复杂的过程,相当复杂!!!
$ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o
看了上面的命令,为什么要将一大堆的文件链接起来才可以得到可执行文件??
且看后面的静态链接与动态链接篇幅。
上面分析了一段程序的执行过程,下面具体看看编译器到底做了什么工作。
编译过程一般可以分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。
1、扫描器(Scanner)的任务就是进行简单的词法分析,运用一种类似于有限状态机(Finite State Machine)的算法将源代码的字符序列分割成一系列的记号(Token)。词法分析产生的记号一般有以下几类:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)。扫描的过程由lex程序完成。
2、语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树(Syntax Tree)。简单来讲,由语法分析器生成的语法树就是以表达式(Expression)为节点的树。语法分析的过程由yacc工具完成。
3、语义分析由语义分析器(Semantic Analyzer)来完成。
----编译器所能分析的语义是静态语义,与之对应的动态语义只有在运行期才能确定。
----静态语义通常包括声明和类型的匹配,类型的转换。
----经过语义分析后,语法分析生成的语法树的表达式都被标识了类型,如果有些类型需要隐式转换,语义分析程序会在语法树中插入相应的转换节点。
4、源代码优化器对源代码进行优化,但直接在语法树上作优化比较困难,往往是将整个语法树转换成中间代码进行优化。
中间代码使得编译器分为前端和后端:前端负责产生机器无关的中间代码;后端负责将中间代码转换成目标机器代码。
5、目标代码优化器将上述生成的目标机器代码进行优化,比如选择合适的寻址方式、使用位移代替乘法运算、删除多余的指令等。
编译器忙活了半天,可生成的目标代码中,我们还不知道函数访问所要的目标函数的地址,变量访问所要的目标变量的地址,这可咋办呢??
其实目标函数访问也好,变量访问也好,这都可以归结为一种方式,就是所谓的模块间符号的引用。
人们把每个源代码模块独立地编译,然后按照需要将它们组装起来,这个组装模块的过程就是“链接”。
链接的主要内容就是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确衔接。
链接过程主要包括地址和空间分配、符号决议和重定位。
相关文章推荐
- 面试准备之Java常用的设计模式
- 2016-春季校招面试笔试mark
- 图论面试题
- 一个大神程序员的使命感究竟应该是什么
- 为什么你投十份简历,只有一两家公司约你?又或者为什么你每投一份简历都能获得面试机会?
- 《程序员修炼之道》——第二章 注重实效的途径(三)
- 经典算法题一览
- 程序员如何优雅的挣零花钱?
- 成为高级程序员的 10 个步骤
- equal() 与hashcode()之我理解
- hibernate中Restrictions用法
- 剑指offer代码解析——面试题25二叉树中和为某一值的路径
- 剑指offer代码解析——面试题25二叉树中和为某一值的路径
- 毁灭程序员效率的 15 个障碍
- 据说年薪30万的Android程序员必须知道的帖子
- Android面试经验汇总(二)
- 某易iOS开发面试题
- 剑指offer代码解析——面试题24二叉搜索树的后序遍历序列
- 剑指offer代码解析——面试题24二叉搜索树的后序遍历序列
- 剑指offer代码解析——面试题23从上往下打印二叉树