zoukankan      html  css  js  c++  java
  • 条件编译,头文件,静态库,共享库与多文件编程

    条件编译

    条件编译即满足某些条件的时候编译某部分代码,常用于开发多个版本的程序,当满足条件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<stdio.h>...对应的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的基础上的, 既然后者是静态链接, 当然还需要包含相应的<dlfcn.h>,但libadd.so里的函数在调用时就不需要相应的头文件了

    Q:-ldl是不是因为使用的<dlfcn.h>???
    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<stdio.h>
    #include<stdlib.h>
    #include<dlfcn.h>
    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
  • 相关阅读:
    php生成excel
    gearmand
    开启Nginx的目录文件列表功能
    zend框架学习
    引用方法形成树
    智能指针实现
    图文例解C++类的多重继承与虚拟继承
    CC++定位崩溃代码行的方法
    C++函数重定义、重载、重写
    勤奋吧,一天一点,努力提高基本技能。
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/5887295.html
Copyright © 2011-2022 走看看