本文所介绍的是like-unix 系统下的相关工做原理,适合于初学者和app开发者。linux
做为通常的码农来讲,大部分时间咱们都在为各类逻辑而煞费苦心,然而你是否利用太短暂的瞬间想过(特别是在集成环境下编程的程序猿),你编写的程序到运行还须要哪些步骤呢?有些码农会说,我想它干啥,编译器都会为我所有处理好。是,如今的编译器确实很强大。可是你编译过程当中是否遇到过错误呢?——特别是大型程序。好比,你是否遇到过这个错误“undefined reference to 【function】(function表明某个函数名称)”呢?当你遇到了错误知道检测程序的哪一个部分吗?。这也是我写此文的目的,不是为了研究如何去编译一个程序,而是当咱们遇到编译错误时,可以快速找到问题所在,及时处理问题。c++
首先咱们明确几个术语:编程
源文件:就是程序猿使用各类编辑器敲出来or拷贝过来的程序猿可以看懂的代码,例如c语言的.c,.h文件,c++的.cpp和.h文件。app
对象文件:源文件经过编译器编译后产生的机器码文件,如.o文件。编辑器
可执行文件:属于对象文件的一种,最后能够被操做系统加载running的文件。函数
码农编写完成的程序到运行大体要进行如下几个步骤:spa
如本例中所示,hello.c就是咱们用c语言编写的程序,称为“源文件”。操作系统
在linux中咱们使用“gcc -o hello hello.c”命令,首先gcc会进行一个“预处理”,包括“include”头文件包含处理、宏定义替换处理和条件编译处理,就是源文件中以“#”开头的代码,都属于预处理器要处理的。unix
当预处理完成后,会生成一个helo.i文件,这个文件也是个text文件——程序猿能看懂的文件。接着编译就上场了,编译器将c语言编写的源程序转换为汇编语言的hello.s文件,此时编译器就能够回家休息了。对象
hello.s文件也是个text文件——通常程序猿可看不懂,要懂汇编语言的才能理解其意思。那么此时的hello.s文件还不是computer能懂的语言,接着汇编器就该出场了。它将hello.s文件编译成对象文件hello.o文件——二进制文件,此时的汇编器也就完成本身的使命了。
既然hello.o已是computer能看懂——程序猿看不懂——的文件了,是否是就能够running了?wait。虽然此时的hello.o文件已是对象文件,但它还不能running 。why?想一想即经典又简单的“hello world”程序(彷佛每种语言讲解书的第一个程序都是它)中是否调用了库中的输出函数(好比c语言中的printf)。咱们的程序中没有对这个函数实现,compter运行到调用输出函数那个指令,怎么会知道它的运行地址呢?这就是链接器的做用了。
链接器的工做就是将一个.o文件中一个函数(不在这个.o文件中定义)的引用和这个函数的定义肯定清楚。简单来说就是将全部的.o文件和引用到的静态库中的模块全都拷贝到一个新的对象文件中,这个新的对象文件中所引用的全部函数和变量都能在其中找到它们的实现。这个文件就是咱们所说的可执行文件。到此该文件就能够被computer加载running了。
可执行文件的组织方式和咱们的源代码会有很大区别,它能够被compter方便的加载到内存中。通常会分为几个section,好比代码 section和数据 section。每一个代码和数据的virtual memory 地址都会被分配好(局部变量在running-time才分配)。
上面hello.c只是一个很简单的例子。在平时的工做中,为了提升代码的可重用性,一个程序通常都会包含不少实现文件和头文件。每一个实现文件编译完成后都会对应一个.o文件(头文件在预处理过程会被拷贝到引用该头文件的实现文件中)。而“undefined reference to 【function】(function表明某个函数名称)”也容易出现,特别是在引用了多个库文件的时候。这就是链接器报出的错误,缘由是链接器找不到这个函数的实现对象文件(即实现文件被编译后的.o文件)。