编译器是讲一种语言翻译成另一种语言的计算机程序。通常源程序称为高级语言,如C/C++,而目标语言则是目标机器的目标代码(有时也称为机器代码)
源程序 ---> 编译器 ---> 目标程序 源文件 ---> 中间目标文件 ---> 可执行文件
一般来说,无论C/C++首先要把源文件编译成中间代码文件,在windows下是.obj, linux下是.o文件,这个动作叫做编译。然后再把大量的.o文件合成执行文件,这个动作叫做链接(link)
编译时,编译器需要做的是语法的正确,函数和变量声明的正确(是否被声明)。对于函数和变量声明,通常是需要告诉编译器头文件所在的位置(头文件中应该只是声明,而定义应该放在C/C++中),只要所有语法正确,编译器就可以编译出中间目标文件。一般来说,没一个源文件都应该对应于一个中间目标文件(o文件)
链接时,主要链接函数和全局变量,所以我们可以使用这些.o文件来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的.o文件。在大多数时候,由于源文件太多,编译生成的.o也很多,而链接时需要明确地指出.o的文件名,这很不方便,于是,我们给中间目标文件打个包,在windows下这种包叫库文件.lib,在linux中就是.a文件
链接后生成的库文件,使用库文件,一方面可以避免在不同项目中重复模块的代码重复;另一方面避免了普通直接copy文件的重用性差的问题。库文件中存放了公共代码的解决方案,其要点就是把公共的,可以被多次复用的目标代码从项目中分离出来,下次在编译(静态库)或者运行时(动态库)可以直接从库文件中提取目标代码即可。
分为静态库和动态库
如果程序是在编译时加载库文件的,就是使用了静态库。如果是在运行时加载目标代码,就成为动态库。
如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,程序的体积会膨胀。如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找库文件和函数体,程序的体积基本变化不大。
静态库的原则是以空间换时间,增加程序体积,减少运行时间,而动态库则相反,增加了运行时间,但减少了程序本身的体积。
静态库文件的扩展名一般为.a,动态库一般以.so(shared object)结尾。
静态库的编写和使用:
静态库的编写:
i> 编写函数代码
ii> 编译生成个目标文件
iii> 用ar文件对目标文件归档,生成静态库文件
注:归档文件必须以lib打头。
静态库的使用:
i> 在gcc 的-I参数后加上静态库头文件的路径。
ii> 在gcc 的-L参数后加上库文件所在的目录
iii> 在gcc 的-l参数后加上库文件名,但要去掉前缀lib和.a扩展名。比如库文件名是libtest.a,那么参数就是 -l test
静态库示例:
myalib.h //静态库头文件
void test();
myalib.c //静态库实现文件
#include <stdio.h>
void test()
{
printf("test\n");
}
step1. 生成目标文件 gcc -c myalib.c (生成myalib.o)
step2. 用ar命令归档,格式为 ar -rc <生成的档案文件名> <.o文件名列表> 再次提醒,归档文件名一定要以lib作为前缀, .a扩展名
ar -rc libtest.a myalib.o (生成libtest.a文件)
使用静态库文件
编写一个测试程序main.c
//main.c 测试静态库调用的程序
#include "myalib.h"
int main(int argc, char* argv[])
{
test();
return 0;
}
编译main.o,这里需要把静态库头文件的路径加到-I参数中
gcc -I /root/exercise -o main.o -c mian.c (生成了main.o)
生成可执行文件,将静态库文件的路径加到-L参数里,并且吧库文件名的lib前缀和.a后缀去掉,加到-l参数后
gcc -o mian -L /root/exercise main.o -ltest
执行./main ,输出 test,说明成功。
动态库的编写和使用
编译生成动态库文件需要加上 -shared -fpic 选项,依然以lib前缀,.so结尾
gcc -fpic -shared -o libtest.so myalib.c 此时就生成了libtest.so