内容简介:对于平常的应用程序开发,我们很少需要关注正是因为集成开发环境的强大,很多系统软件的现在我们通过一个C语言的经典例子,来具体了解一下这些机制:
对于平常的应用程序开发,我们很少需要关注 编译
和 链接
过程。我们平常 Xcode
开发就是集成的的开发环境 (IDE)
,这样的IDE一般都将 编译
和 链接
的过程一步完成,通常将这种 编译
和 链接
合并在一起的过程称为 构建
,即使使用命令行来编译一个源代码文件,简单的一句 gcc hello.c
命令就包含了非常复杂的过程!
正是因为集成开发环境的强大,很多系统软件的 运行机制与机理
被掩盖,其程序的很多莫名其妙的错误让我们无所适从,面对程序运行时种种性能瓶颈我们束手无策。我们看到的是这些问题的现象,但是却很难看清本质,所有这些问题的本质就是 软件运行背后的机理及支撑软件运行的各种平台和工具
,如果能深入了解这些机制,那么解决这些问题就能够游刃有余。
编译流程分析
现在我们通过一个 C语言 的经典例子,来具体了解一下这些机制:
#include <stdio.h> int main(){ printf("Hello World"); return 0; } 复制代码
在 linux 下只需要一个简单的命令(假设源代码文件名为hello.c):
$ gcc hello.c $ ./a.out Hello World 复制代码
其实上述过程可以分解为四步:
- 预处理(Prepressing)
- 编译(Compilation)
- 汇编(Assembly)
- 链接(Linking)
预编译
首先是源代码文件 hello.c
和相关的头文件(如 stdio.h
等)被预编译器 cpp
预编译成一个 .i
文件。第一步预编译的过程相当于如下命令(-E 表示只进行预编译):
$ gcc –E hello.c –o hello.i 复制代码
还可以下面的表达
$ cpp hello.c > hello.i 复制代码
预编译过程主要处理源代码文件中以”#”开头的预编译指令。比如 #include、#define
等,主要处理规则如下:
- 将所有的
#define
删除,并展开所有的宏定义 - 处理所有条件预编译指令,比如
#if,#ifdef,#elif,#else,#endif
- 处理
#include
预编译指令,将被包含的文件插入到该预编译指令的位置。 - 删除所有的注释
//
和/**/
- 添加行号和文件名标识,比如
#2 “hello.c” 2。
- 保留所有的
#pragma
编译器指令
截图个大家看看效果
经过预编译后的文件 (.i文件)
不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经插入到 .i文件
中,所以当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题。
编译(compliation)
编译过程就是把预处理完的文件进行一系列的: 词法分析
、 语法分析
、 语义分析
及 优化后生产相应的汇编代码文件
,此过程是整个程序构建的核心部分,也是最复杂的部分之一。其编译过程相当于如下命令:
$ gcc –S hello.i –o hello.s 复制代码通过上图我们不难得出,通过命令得到汇编输出文件
hello.s
.
汇编(assembly)
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎对应一条机器令。
所以汇编器的汇编过程相对于编译器来讲比较简单,它没复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了。其汇编过程相当于如下命令:
as hello.s –o hello.o 复制代码
或者
gcc –c hello.s –o hello.o 复制代码
或者使用 gcc
命令从 C源代码文件
开始,经过预编译、编译和汇编直接输出目标文件:
gcc –c hello.c –o hello.o 复制代码
链接(linking)
链接通常是一个让人比较费解的过程,为什么汇编器不直接输出可执行文件而是输出一个目标文件呢?为什么要链接?下面让我们来看看怎么样调用 ld
才可以产生一个能够正常运行的 Hello World
程序:
注意默认情况没有gcc / 记得 : $ brew install gcc 复制代码
链接相应的库
下面在贴出我们的写出的源代码是如何变成目标代码的流程图:
主要通过我们的编译器做了以下任务: 扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化
到这我们就可以得到以下的文件,不知道你是否有和我一起操作,玩得感觉还是不错,继续往下面看
iOS的编译器
iOS现在为了达到更牛逼的速度和优化效果,采用了 LLVM
LLVM采用三相设计,前端Clang负责解析,验证和诊断输入代码中的错误,然后将解析的代码转换为LLVM IR,后端LLVM编译把IR通过一系列改进代码的分析和优化过程提供,然后被发送到代码生成器以生成本机机器代码。
编译器前端的任务是进行:
- 语法分析
- 语义分析
- 生成中间代码(intermediate representation )
在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。
以上图解内容所做的是事情和 gcc
编译一模模一样样!
iOS程序-详细编译过程
- 1.写入辅助文件:将项目的文件结构对应表、将要执行的脚本、项目依赖库的文件结构对应表写成文件,方便后面使用;并且创建一个
.app
包,后面编译后的文件都会被放入包中; - 2.运行预设脚本:
Cocoapods
会预设一些脚本,当然你也可以自己预设一些脚本来运行。这些脚本都在Build Phases
中可以看到; - 3.编译文件:针对每一个文件进行编译,生成可执行文件
Mach-O
,这过程LLVM
的完整流程,前端、优化器、后端; - 4.链接文件:将项目中的多个可执行文件合并成一个文件;
- 5.拷贝资源文件:将项目中的资源文件拷贝到目标包;
- 6.编译
storyboard
文件:storyboard
文件也是会被编译的; - 7.链接
storyboard
文件:将编译后的storyboard
文件链接成一个文件; - 8.编译
Asset
文件:我们的图片如果使用Assets.xcassets
来管理图片,那么这些图片将会被编译成机器码,除了icon
和launchImage
; - 9.运行
Cocoapods
脚本:将在编译项目之前已经编译好的依赖库和相关资源拷贝到包中。 - 10.生成
.app
包 - 11.将
Swift
标准库拷贝到包中 - 12.对包进行签名
- 13.完成打包
编译过程的确是个比较复杂的过程,还有链接!并不是说难就不需要掌握,我个人建议每一个进阶路上iOS开发人员,都是要了解一下的。不需要你多么牛逼,但是你能在平时的交流讨论,面试中能点出一个两个相应的点,我相信绝对是逼格满满!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 预处理器、编译器、汇编、链接
- C语言的编译链接过程的介绍
- Linux 环境下 gcc 链接库 编译、链接(概览) 以及 自动化工具Makefile的编写
- VS2013编译Lua5.3.x动态链接库
- Webpack如何引入CDN链接来优化编译后的体积
- 链接时找不到C库,错误编译`boost :: program_options`的例子
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。