条件编译,头文件,静态库,共享库与多文件编程

2019-07-13 06:30发布

本文转自嵌入式Linux中文站

条件编译

条件编译即满足某些条件的时候编译某部分代码,常用于开发多个版本的程序,当满足条件A时,编译出免费版本的软件,当满足条件B时,编译除vip版本的软件,可以提高代码的复用率。条件编译使用"预处理命令+宏定义"来实现,更多宏命令参见 $vi tutu.c
#ifdef VIP    //也可以写成
#if defined (VIP)
//把免费版改造成VIP版的代码
#elif defined PRO//把免费版改造成PRO版代码
#endif//免费版本的代码

$gcc -DVIP tutu.c    //将编译出VIP版的软件

头文件header

头文件的编写

C语言的标识符在使用之前一定要声明,把所有的标识符的声明都放在一个头文件中,在预处理阶段把这些声明一股脑的复制到源文件的开头,这样任何标识符在使用之前不就都被声明过了。但这个方案引起了一个问题,就是如果我调用了fcn.c的函数,包括了它的头文件fcn.h,我的同事也这么做,那么编译的文件中不就有了两份fcn.h,而一个项目数百的文件,每个文件都有自己的头文件,还会有相互调用的问题,这样编译器的压力就会很大,所以就有了"头文件卫士": #ifndef __FCN_H
#define __FCN_H
   //fcn.c里标识符声明

#endif    //__FCN_N
这三句话就保证了这个头文件在整个程序中只有一份,因为一旦第一次使用这个头文件的时候,__FCN_H还没有被定义,那么他就会被定义,里面的声明代码也会被使用,如果再由文件使用这个头文件,那么由于__FCN_H已经被定义了,条件编译的条件不满足,所以里面的声明代码也就不会再被使用,反正声明有一份就够了。

头文件的使用

头文件需要使用#include宏命令把头文件原封不动的复制到当前文件夹 #include<标准头文件> //在默认头文件路径里查找头文件,如果找不到就报错
#include"头文件路径" //按照指定的路径查找头文件,找不到就到默认头文件路径查找,还是找不到就报错

静态库

静态库:由若干个.o目标文件打包生成的.a文件叫静态库文件, 链接静态库就是将被调用的代码指令到调用模块中,并体现在最终的可执行文件中 ,静态库只能静态链接,
优势
  • 不需跳转,执行效率较共享库高一些
  • 使用静态库的代码在运行时不需要依赖静态库
劣势
  • 静态库占用空间比较大,多次链接(多次复制)之后最终生成的可执行文件比较大
  • 修改维护不方便,库文件改一点,链接它的文件就得从头复制一遍
生成静态库
  1. 编写.c文件 $vi add.c
  2. 生成.o文件 $cc -c add.c
  3. 生成静态库文件 $ar –r libadd.a add.o
  4. 链接静态库文件 $cc -o main main.o -static -ladd -L.
    Note:
  • 对于临时使用不在公用库目录的库,链接前可以把库的路径添加到LIBRARY_PATH,$export LIBRARY_PATH=$LIBRARY_PATH:`pwd`
  • 或者使用直接链接$cc main.o ./libadd.a
  • 或者使用编译选项链接$cc main.o -ladd –L.
  • 和动态库不同,静态库不存在运行时找不到的问题,编译时就把所有库问题解决了。
  • add是库名,libadd不是,是文件名

共享库

共享库就是由若干个目标文件打包生成的xxx.so文件 ,链接共享库不是将被调用代码指令复制到调用模块中,而是将被调用代码指令在共享库中的相对地址复制到调用模块中, 体现在最终的可执行文件中,不论静态链接还是动态链接(共享库可以静态链接也可以动态链接)。 Linux下进行链接的缺省操作是先考虑动态链接库,即如果同时存在静态和共享库,不特别指定的话,将与共享库相连接 优势
  • 共享库占用空间比较小, 生成的可执行文件比较小, 即使修改了库中的代码, 只要相对地址/接口保持不变, 则不需要重新链接.
劣势
  • 使用共享库的代码在运行时需要依赖共享库, 并且执行效率较静态库低。
生成共享库
  1. 编写.c文件 $vi add.c
  2. 生成.o文件 $cc -c -fpic add.c
  3. 生成共享库文件 $cc -shared -o libadd.so add.o
  4. 链接共享库 $cc -o main main.o -ladd -L.
Q:如果本文件夹有了libdl.a或者libdl.so会链接本文件夹的还是系统默认的???
A:如果使用$gcc main.o -L. -ldl当然会链接本文件夹的, 你当-L.是空气啊 Note:
  • -fpic是生成位置无关码的选项, 即生成相对地址
  • $ldd a.out #查看a.out所依赖的共享库信息
  • 对于临时使用的不在公用库目录的共享库,链接后可以把库的路径添加到LD_LIBRARY_PATH:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`
  • 如要永久更改动态库的搜索目录,可以创建文件/etc/ld.so.conf.d/my.config并将路径写入其中,再使用$ldconfig /etc/ld.so.conf.d/my.config配置链接选项
  • 与静态库不同,修改环境变量和配置文件都是用来解决运行时找不到库的问题,并不针对编译时链接的问题,编译时还是要使用-L.

静态链接

静态链接,即编译时链接,使用-static可以强制链接静态库
Q:静态链接时调用函数需要包含相应库的头文件吗?
A:需要. 链接我们熟知的#include...对应的libc.so时就是使用的静态链接, 默认都是静态链接共享库,也可以使用$gcc –static强制链接静态库libc.a,任何一个库都有两个版本:.a版本和.so版本 静态链接:
  1. 编写.c文件 $vi main.c
  2. 生成.o文件 $cc -c main.c
  3. 链接库文件 $cc main.o –ladd
Note:
  • 开发时,使用静态链接,以便gcc能够找到编译时需要的共享库。
  • 发布时,使用动态链接,以便程序加载运行时能够自动找到需要的共享库。

动态链接

动态链接:运行时链接,建立在静态链接libdl.so(a)库的基础上, 程序在运行过程中动态地链接共享库,所以需要在源代码中写链接代码,编译器是无能为力了,只能帮到dl库了
Q:动态链接时调用函数需要包含相应库的头文件吗?
A:动态链接libadd.so是建立在静态链接 libdl.so的基础上的, 既然后者是静态链接, 当然还需要包含相应的,但libadd.so里的函数在调用时就不需要相应的头文件了 Q:-ldl是不是因为使用的???
A:是,man dlopen,这个库包含实现动态链接的代码
ATENTION:动态链接和静态链接不是并列关系,是依存关系, 没有静态链接的libdl.so(a), 动态链接就是个屁 Q: 为什么共享库要有执行权限, 静态库不需要
A:因为共享库在运行时使用,静态库在编译时里面的代码已经链接到程序里了,运行时和静态库就没关系了 动态连续需要静态链接dl.so库:
  1. 编写.c文件 $vi main.c
  2. 生成.o文件 $cc -c main.c
  3. 链接库文件 $cc main.o –ldl
动态链接的源文件 #include
#include
#include

int main(){
 void *handle=dlopen("../libfcn.so",RTLD_NOW);
 if(NULL==handle)
   printf("%s",dlerror()), exit(-1);          int (*pAdd)(int,int)=(int (*)(int,int))dlsym(handle, "add");
 if(NULL==pAdd)          printf("%s",dlerror()),exit(-1);      printf("%d ",pAdd(1,2));      int res=dlclose(handle);
 if(0!=res)
   printf("%s",dlerror()),exit(-1);      return 0; }  
Note:
  • -l是静态链接库名选项, dl是共享库名(libdl.so), 用来实现我们程序中的动态加载卸载libadd.so