编译和连接

写在前面

只讨论C语言。编程

“二进制代码”——指被可以计算机直接执行的代码(至于到底是操做系统层的命令,仍是CPU的机器指令,这里不作深刻讨论)。函数

头文件

通常是.h文件。编码

头文件的功能在于指示编程人员和编译器:哪些函数能够调用?这些函数应该怎么调用(参数和返回值)?操作系统

头文件不指示任何库的存储位置。全部须要的库文件,应位于默认目录或指定目录。指针

中间代码文件

通常是.o或.obj文件。code

中间代码文件包括:接口

  • 通常语句的二进制编码。
  • 用户定义函数的二进制编码和接口。
  • 函数调用的指示(能够理解为“连接”或“指针”)。

中间代码文件的使命在连接完成后就结束了。进程

库是一个文件。内存

库的内容是造成库的若干个中间代码文件内容的集合。编译器

库和中间代码文件的格式不同。

静态库

在Windows中被称为“静态连接库”。

静态库本质上是一个库。在Linux中通常为.a文件,在Windows中为.lib文件。

假设若干个可执行文件在连接时使用了相同的静态库。实际上,静态库产生的二进制编码被放进了可执行文件中。当这些可执行文件运行的时候,每一个可执行文件产生的进程,在内存中各有一份上述二进制编码的副本。

静态连接库的使命在连接完成后就结束了。

同一个可执行文件产生的进程在内存中共用一份代码段。这是Linux操做系统内存管理的特性,与静态库无关。

共享库

在Windows中被称为“动态连接库”。

共享库本质上是一个库。在Linux中通常为.so文件,在Windows中为.dll文件。

假设若干个可执行文件在连接时使用了相同的共享库。实际上,共享库被可执行文件所引用,其二进制编码并无被放进可执行文件中。当这些可执行文件运行时,被不一样可执行文件使用的、同一个由共享库产生的二进制编码,在内存中只有一个副本。这个副本由全部可执行文件的全部进程共用。

静态库和共享库的格式不一样,二者的大小不具备可比性。

编译与连接

编译

过程:由源文件生成中间代码文件。

命令:gcc

选项:-c

打包静态库

过程:用中间代码文件生成静态库。

命令:ar

选项:-r

打包共享库

过程:用中间代码文件生成共享库。

命令:gcc

选项:-shared -fPIC

注意:

  • 在编译中间代码文件时和打包共享库时,都必须添加上述选项!不然打包会出错。
  • 若不加-fPIC,则生成“伪共享库”。“伪共享库”既不会被放入可执行文件,又不能被多个引用其的可执行文件共享。

静态连接

过程:用中间代码文件和静态库(可无)生成可执行文件。

结果:可执行文件包含运行所须要的所有二进制代码(包括被调用的系统函数的二进制代码)。可执行文件通常能够独立运行。

命令:gcc

选项:-static

动态连接

过程:用中间代码文件、静态库(可无)和共享库(可无)生成可执行文件。

结果:

  • 可执行文件包括中间代码文件和静态库中的二进制代码,这些二进制代码来源于:a.基本语句;b.自定义函数的实现。
  • 可执行文件依赖于:连接时使用到的共享库(全部层次的,所有的)。
  • 可执行文件通常不能独立运行。

命令:gcc

选项:无

其余

静态编译 = 编译 + 静态连接

动态编译 = 编译 + 动态连接

可使用ldd命令来查看可执行文件所依赖的共享库。

gcc命令的其余经常使用选项:

  • -Wl,-rpath,./:可执行文件运行时,将所在目录加入共享库的查找范围。
  • -include:查找头文件并加入编译。
  • -I(大写的i):将目录的全部头文件加入编译。
  • -l(小写的L):查找库并加入编译(后面紧跟库的缩写)。
  • -L:将目录加入库的查找范围。应将-L放在gcc命令的最后,不然某些时候会出错。

GCC会自动地将一些头文件和库加入编译和连接:

  • 头文件的默认查找目录:当前目录、/include//usr/include/
  • 库的默认查找目录:当前目录、/lib//usr/lib/
相关文章
相关标签/搜索