1.编译过程1.预处理gcc -E hello.c -o hello.i源码文件和相关头文件等,被预编译器 cpp 预编译成一个 .i 文件。预编译过程主要处理那些源代码文件中的以 "#" 开始的预编译命令。如 "#include", "define"等,规则如下:1.将所有的 "#define" 删除,并且展开所有的宏定义2.处理所有条件预编译指令,比如 "#if", "#ifdef", "#elif", "#else", "#endif"3.处理 "#include" 预编译指令,将被包含的文件插入到该预编译指令的位置4.删除所有的注释 "//" 和 "/* */"5.添加行号和文件名标识,比如 #2 "hello.c" 2, 以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时能够产生行号6.保留所有的 #pragma 编译器指令,因为编译器必须要使用它们2.编译gcc -S hello.i -o hello.s编译的过程就是把预编译处理完的文件进行一系列的词法分析,语法分析,语义分析以及优化后生成响应的汇编代码文件。3.汇编gcc -c hello.s -o hello.o汇编器是将汇编代码转变为机器可执行的指令,每个汇编语句几乎都对应一条机器指令。4.链接ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.oarray[index] = (index + 4) * (2 + 6)2.编译器做了啥编译过程:1.词法分析(生成 token)源代码被输入到 扫描器(scaner),扫描器的任务很简单,它只是简单的进行词法分析,运用一种类似于有限状态机的算法可以很轻松的将源代码的字符串序列分割成一系列的 记号(token) 。词法分析产生的记号一般可以分为如下几类:关键字,标识符,字面量(包含数字,字符串等)和特殊符号(如加好,等号)。有个叫 lex 的程序可以实现词法扫描.array[index]=(index+4)*(2+6)2.语法分析(生成语法树)接下来语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树。整个分析过程采用了上下文无关语法的分析手段。语法分析器生成的语法树就是以表达式为节点的树。yacc 词法分析工具3.语义分析(还是语法树)语义分析器(Semantic Analyzer)仅仅是完成了对表达式的语法层面的分析,但是它并不了解这个语句是否真正有意义。编译器所能分析的语义是静态语义,所谓的静态语义是指在编译期可以确定的语义,与之对应的是动态语义就是只有在运行期才能确定的语义。静态语义通常包括声明和类型的匹配,类型的转换。动态语义一般是指在运行期出现的语义相关的问题,比如将0作为除数是一个运行期语义错误。4.中间语言生成(对语法树进行优化,生成与机器无关的代码)现代的编译器有着很多层次的优化,往往在源代码级别会有一个优化的过程。我们这里描述的源码级优化器( Source Code Optimizer) 在不同的编译器中可能会有不同的定义或有一些其他的差异。比如表达式 (2 + 6)就可以优化,编译期就可以确定。我们看到 (2 + 6)这个表达式被优化成8。其实直接在语法树上面优化比较困难,所以源代码优化器往往是将整个语法树转换成成中间代码(Intermediaet Code),它是语法树的顺序表示,其实它已经非常接近目标代码了。但是它一般跟目标机器和运行时环境是无关的,比如它不包含数据的尺寸,变量地址和寄存器的名字等。中间代码有很多种类型,在不同的编码器中有不同的形式,比较常见的有:三地址码(Three-address Code)和 P-代码(P-code)。三地址码: x = y op z上面的语法树可以被翻译成三地址码 :t1 = 2 + 6   t2 = index + 4t3 = t2 * t1array[index] = t3t1 这个时候会被优化成 8, 优化后为:t2 = index + 4t2 = t2 * 8array[index] = t2中间代码使得编译器可以被分为前端和后端。编译器前端负责产生机器无关的中间代码,编译器后端将中间代码转换成目标机器代码。这样对于一些可以跨平台的编译器而言,它们可以针对不同的平台使用同一个前端和针对不同机器平台的数个后端。5.目标代码的生成和优化源代码级别优化器产生中间代码,标志着下面的过程都属于编译器后端。编译器后端主要包括代码生成器(Code Generator) 和 目标代码优化器( Target Code Optimizer)。代码生成器将中间代码转换成目标机器代码,这个过程十分依赖于目标机器,因为不同的机器有着不同的字长,寄存器,整数数据类型和浮点数据类型。有可能的代码:movl    $.LC0, %edimovl    $0, %eaxcall    printfmovl    $0, %eaxpopq    %rbp.cfi_def_cfa 7, 8最后目标代码优化器对上述的目标代码进行优化,比如选择合适的寻址方式,使用位移来代替乘法运行,删除多余的指令等。忙活了这么多步骤,index 和 array 的地址还没确定。目标代码中有变量定义在其他模块,怎么办?事实上,定义其他模块的全局变量和函数在最终运行时的绝对地址都要在最终链接时才能确定。所以现代的编译器可以将一个源代码文件编译成一个未链接的目标文件,然后由链接器最终将这些目标文件链接成可执行文件。
1.一个程序被分割成多个模块后,这些模块之间如何形成单一程序是必须解决的问题。模块之间如何组合的问题,可以归结为模块之间如何通信的问题。常见的方式:1.模块间函数的调用2.模块间变量的访问函数访问需要知道函数的地址,变量访问也需要变量的地址。所以2种方式归结为一种,那就是对模块间符号的引用。2.模块拼装---静态链接链接的主要内容就是把各个模块之间互相引用的部分处理好,使得各个模块之间能够正确的衔接。链接过程包括了:1.地址和空间的分配2.符号决议符号决议有时候也叫符号绑定,名称绑定,名称决议,甚至叫地址绑定,指令绑定。大体上是一样的。但从细节上区分,它们还是存在一定差异,比如 "决议"更倾向静态链接,而 "绑定"更倾向于动态链接,即它们所指的范围不一样。在静态链接我们统一称为符号决议。3.重定位静态链接的基本过程和作用 :目标文件和库一起链接,最终形成可执行文件。而最常见的库就是运行时库,它是支持程序运行的基本函数的集合。库其实是一组目标文件的包,就是一些最常用的代码编辑成目标文件后打包存放。比如我们在程序模块 main.c 中使用了另外一个模块 func.c 中的函数 foo()。我们在 main.c 模块中每一处调用 foo 的时候就必须知道foo 这个函数的地址。但是由于每个模块都是单独编译的,编译器编译 main.c 的时候并不知道 foo 函数的地址,所以它暂时把这些调用 foo的指令的目标地址搁置,等待最后链接的时候,由链接器去将这些指令的目标地址修正。如果没有链接器,须要我们手工把每个调用的 foo 的指令修改,填入正确的 foo 函数地址。当 func.c 模块被重新编译的时候,foo 函数地址可能被改变,那么我们在 main.c 中所有使用到的 foo 的地址指令将全部重新调整。这些繁琐的工作将成为程序员的噩梦。使用链接器,你可以直接引用其他模块的函数和全局变量而无需需要它们的地址,因为链接器在链接的时候,会根据你引用到的 foo 的指令重新修正。让它们的目标地址为真正的 foo 函数地址。

1.预编译

2.编译

3.汇编

4.链接

5.词法分析

6.语法分析

7.语义分析

8.中间语义生成

9.目标代码生成和优化

2.编译器

2.程序员的自我修养---编译和链接相关推荐

  1. 《程序员的自我修养-Ch7_动态链接》

    Ch7 动态链接 7.1 为什么要动态链接 1 静态链接的缺点 1.1 内存和磁盘空间占用 **每个程序都将包含的库函数直接打包使用,使得占用内存和磁盘大.**如下图1所示,其中Program1和Pr ...

  2. 程序员的自我修养--编译链接资料收集

    1.龙书(Dragon book)  英文名:Compilers: Principles,Techniques,and Tools  作者:Alfred V.Aho,Ravi Sethi,Jeffre ...

  3. 《程序员的自我修养》

    <程序员的自我修养>这本书偏底层,来来回回读了有三四遍了,每一次都有新的收获,不过很快又会忘记,所以写下了这本书从17年12月份至今的全书的笔记,留作以后自己复习. 第二章:编译和链接 源 ...

  4. 腾讯朋友力荐书籍:程序员的自我修养:链接、装载与库

    后台开发需要学习底层知识,只有底层知识掌握了,学一些中间件是信手捏来,中间件也是跑在底层的操作系统上.<<程序员的自我修养:链接.装载与库>>对学习底层知识非常有帮助,腾讯的朋 ...

  5. 程序员的自我修养,好文

    很认真的聊一聊程序员的自我修养 本文正在筹备第二版,以下是第一版主要内容 首先要谈的是,今天的话题所聊的程序员包含哪些人? 在中国,写程序,不仅仅是一种兴趣,更多的时候,还是一种普通职业和谋生工具 大 ...

  6. 很认真地聊一聊程序员的自我修养

    最近看了<浅谈程序员的英语学习>,对于文章中的观点我非常认同,英语是非常重要的,但文章站的高度还是太高,具体表述的学习方法我不是很认同,也认为不太实际,恰好之前有一篇一直没有发表的文章想重 ...

  7. 程序员的自我修养(转载)

    疲于加班,幸好我还保留看看别人写的东西的习惯,下面转载的一篇文章,在某些地方触动到了我,有些时候我也是反面典型,思考得少,反而直接拿来用的更多,但是我发现,往往很多问题都是糊里糊涂地用了别人的东西导致 ...

  8. 读书笔记程序员的自我修养 0

    读书笔记<<程序员的自我修养>> 0 为什么要读这本书? 可能因为自己是读硬件的缘故,对于编程,我总是尝试的了解各种表象的下面发生了什么事情.而困扰了我的许多问题,在这本书上都 ...

  9. 程序员的自我修养阅读笔记

    编译和链接 将编译和链接合并到一起的过程称为构建(Build). 从源文件生成最终可执行目标文件共有4个步骤: 预处理(Prepressing) 编译(Compilation) 汇编(Assembly ...

  10. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(三)】函数调用与栈(this指针、返回值传递临时对象构建栈、运行库与多线程、_main函数、系统调用与中断向量表、Win32、可变参数、大小端

    文章目录 前言 介绍 内存 内存布局 栈与调用惯例 堆与内存管理 运行库 入口函数和程序初始化 C/C++运行库 运行库与多线程 C++全局构造与析构 fread 实现 系统调用与API 系统调用介绍 ...

最新文章

  1. Statement与PreparedStatement区别
  2. mysql 员工工资上涨5%_工资从1万到3万,你还差mysql数据库优化之系列五
  3. halcon/c++接口基础 之异常处理
  4. webbrowser设置横向打印_C# 日常记录:指定打印机/纸张/纸盒(静默打印)(不弹窗打印)WinForm篇...
  5. 吴恩达神经网络1-2-2_图神经网络进行药物发现-第1部分
  6. Python入门--闭包,工程函数
  7. qt修改文件编码格式
  8. Android学习笔记——用户界面开发进阶(多个示例记录)
  9. Deecamp考试给我的启发
  10. 使用Voxelmorph配准IXI:数据预处理之颅骨去除及仿射对齐
  11. android 测试 内存,Android性能测试之内存(二)
  12. 深入解读RFM模型-实战应用干货
  13. scrapy抓斗鱼主播的图片
  14. #ArcGis中如何对属性表中的字段进行顺序赋值??
  15. liunx篇---测试过程中什么时候会用到liunx。常用的命令有哪些?
  16. 【Win 10应用开发】如何知道UAP在哪个平台上运行
  17. JMeter-接口自动化测试读取用例,执行并结果回写
  18. Android代码 listview选中,android自定义listview的选中状态
  19. 【软件工程】 软件需求分析
  20. k8s(四):核心技术-Controller

热门文章

  1. python之socket网络编程
  2. 第八回 新年晚会艺压群芳 文理分科三人聚首[林大帅作品选]
  3. java swing 复选JCheckBox组件美化
  4. Flex4的皮肤skin
  5. 如何写出整洁规范的R代码?是时候讨论一下代码规范性了
  6. 【R可视化】你家乡的肯德基都在哪儿?
  7. html教程作用,HTML段落的作用及教程
  8. 【转】Js 数组转JSON格式
  9. 关于MySQL latch争用深入分析与判断
  10. 什么是多态,多态的实现方法是什么?