C语言(Head First C)-9_2:静态库与动态库:动态库

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 9_2:静态库与动态库:动态库

 

 场景:

    丈量长度的仪器值数显示,在不同的国家显示不同的语言和单位,比如在中国可以用米meter,在英国可以用英尺feet;

    假设有两种不同的仪器,一种测量比较大的物体,一种测量比较小的物体;分别叫machinesmall,machinebig;


 我们为每种仪器编写自己的软件,使其可以显示测得的数据;

 

 已有的模块9_1-hfcal库可以生成测量的数据值,用于显示;我们创建一个测试程序:

 (Code9_1)

(文章末尾附上了目录结构)

9_1-hfcal.c

#include <stdio.h>
#include <9_1-hfcal.h>

void dispaly_length(float length){
    
    printf("Length: %.2f meter.\n",length);

}
./9_1-include/9_1-hfcal.h

void dispaly_length(float length);
9_1-test.c

#include <stdio.h>
#include <9_1-hfcal.h>

int main() {

    dispaly_length(115.24);
    
    return 0;
}


 编译、存档:

 bogon:0911-1 huaqiang$ touch 9_1-hfcal.h

 bogon:0911-1 huaqiang$ touch 9_1-hfcal.c

 bogon:0911-1 huaqiang$ gcc -c 9_1-hfcal.c -I .

 bogon:0911-1 huaqiang$ ar -rcs lib9_1-hfcal.a 9_1-hfcal.o

 bogon:0911-1 huaqiang$ gcc 9_1-test.c -I . -L . -l9_1-hfcal -o 9_1 && ./9_1

 log:

 Length: 115.24 meter.

 

 如果头文件位于9_1-include目录,目标文件放到9_1-libs目录下,编译、存档、链接可以这样写:

 gcc -c 9_1-hfcal.c -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0911-1/9_1-include

 ar -rcs /Users/huaqiang/HQDSwiftDemo/C_Head_First/0911-1/9_1-libs/lib9_1-hfcal.a 9_1-hfcal.o

 gcc 9_1-test.c -I/Users/huaqiang/HQDSwiftDemo/C_Head_First/0911-1/9_1-include -L/Users/huaqiang/HQDSwiftDemo/C_Head_First/0911-1/9_1-libs -l9_1-hfcal -o 9_1

 

 问题来了:

    因为要在不同的国家显示,需要使用不同的语言和单位;

    仪器的种类也有许多不同,比如50种;

    如果为每一种机器都编写各种语言的程序,简直了!如果要修改程序岂不是要修改好多;

 

 程序由碎片组成:

    程序是由不同的目标代码组件而成的;

    先创建.o文件和.a存档,然后再把它们链接成可执行程序;

 

 一旦链接,就不能改变:

    这种方式有个问题,就是程序是静态的;各个独立的目标文件链接成的可执行程序,没办法修改“原料”,除非重新构建整个程序;

 

 在运行时动态链接:

    之所以不能修改可执行文件中的目标代码,是因为它们在编译程序时静态链接在了一起;

    那么,如果组成程序的多个文件,可以在运行前链接到一起,就可以避免这个问题;

 

 可以把目标代码分别保存在单独的文件中,在程序运行时才把它们动态链接到一起;

 

 .a能在运行时链接吗:

    .o和.a文件本身就是独立的文件,使用它们在运行时动态链接是否可行呢?

    不行,普通目标文件和存档包含的这点信息还不足以让它们在运行时链接,动态库文件还需要其他东西,例如要链接的文件名;

 

 动态库——加强版目标文件:

    动态库和我们创建的.o目标文件有点像,又有区别;

    和存档也很像,也可以从多个.o目标文件创建;不同的是,这些目标文件在动态库中链接成了一段目标代码;

 

 小结动态库:

    有元信息的可重定位目标文件;

    动态库由一个或多个.o文件创建;

    动态库有一些额外信息,操作系统需要用这些信息把库链接到程序;

    动态库的核心是一段目标代码;

 

 如何创建自己的动态库:

 (Code9_2)

 9_2-hfcal.h

void dispaly_length(float length);
 9_2-hfcal.c

#include <stdio.h>
#include <9_2-hfcal.h>

void dispaly_length(float length){
    
    printf("Length: %.2f meter.\n",length);

}
9_2-test.c

/*
 *
 */

#include <stdio.h>
#include <9_2-hfcal.h>

int main() {

    dispaly_length(115.24);
    
    return 0;
}


9_2-hfcal_en.c

#include <stdio.h>
#include <9_2-hfcal.h>

void dispaly_length(float length){
    
    printf("Length: %.2f feet.\n",length * 0.2);

}

 头文件和源文件都在当前目录;

 

 首先,创建目标文件:

    gcc -I . -fPIC -c 9_2-hfcal.c

    这次,我们在创建.o文件时多加了一个标志:-fPIC;

    它的作用是告诉gcc你想创建位置无关代码;这样它们才能在运行时被决定加载到存储器的某个位置;

    位置无关代码可以在存储器中搬来搬去;

 

 说明:

    事实上,大多数操作系统都不需要加这个选择;一些操作系统在加载动态库时会使用一种叫做存储器映射的技术,也就是说代码其实都是位置无关的,比如windows;

 

 一种平台一个叫法:

    大多数OS都支持动态库,工作方式也基本相同,但称呼却差很多;

    windows——动态链接库,后缀.dll;

    Linux和Unix——共享目标文件,后缀.so;

    Mac——动态库,后缀.dylib;

 

 创建方法:

    我们新建一个目录9_2-libs用来存放库文件;

    gcc -shared 9_2-hfcal.o -o ./9_2-libs/lib9_2-hfcal.dylib

    注意:./9_2-libs的方式很好,'.'指代当前目录,这样就避免了写绝对路径的方式;


 -shared:

    告诉gcc把目标文件转化为动态库;

    编译器在创建动态库时会把名字保存在文件中,一旦用某个名字编译了库,就不能再改了,这点很重要;

    一些古老的Mac系统上,没有-shared选项,可以使用-dynamiclib代替;

 

 编译程序:

    创建了动态库就可以像静态库那样使用它;

    gcc -I . 9_2-test.c -L ./9_2-libs -l9_2-hfcal -o 9_2 && ./9_2

 log:

    Length: 115.24 meter.

 

 小结:

    尽管使用的命令和静态库存档一模一样,但两者编译方式不同;

    动态库在编译时,不会再可执行文件中包含库代码,而是插入一段用来查找库的“占位符”代码,并在运行时链接库;

 

 Mac上指定的目标文件路径让程序知道,启动后可以去哪里找它;

 Linux(多数Unix中)上稍不同,编译器只会记录libxxx.so库的文件名,不包含路径;

    即,不把hfcal库保存到标准目录,程序就找不到它;

    所以,Linux会检查保存在LD_LIBRARY_PATH变量中的附加目录;

    只有把库目录加到该变量中,并export它,程序才能找到库;

 

 环境变量的好处是方便使用不同版本的库,尤其是在开发新库时;

 

 export命令:

    Linux命令,用来将自定义变量设为环境变量;

    如:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/libs

    之后在执行程序;

    当然,如果动态库已经在标准目录中,就不需要这样做了;

 

 windows的话,和Linux类似,需要配置PATH环境变量;

 这也是绝大多数动态库会保存到标准目录下的原因;

 

 现在到了使用动态库的时候了:

    我们针对英国的使用标准修改源文件,为了对比说明我们重新定义了源文件 9_2-hfcal_ch.c;

    gcc -c -fPIC 9_2-hfcal_en.c -I .

    gcc -shared 9_2-hfcal_en.o -o ./9_2-libs/lib9_2-hfcal_en.dylib

    gcc -I . 9_2-test.c -L ./9_2-libs -l9_2-hfcal_en -o 9_2-en && ./9_2-en

 log:

    Length: 23.05 feet.

 

 这里为了说明使用了新的源文件生成了新的动态库;

 实际使用可以是:在英国使用的程序链接的动态库是我们修改之后的,这样不必修改代码就可以正确运行了;

 如:

    生成同名动态链接库,替换原有的;

    gcc -shared 9_2-hfcal_en.o -o ./9_2-libs/lib9_2-hfcal.dylib

    运行之前的目标程序;

    ./9_2

log:

    Length: 23.05 feet.

    和我们修改之后的9_2-en运行结果是一样的;

 

 小结:

    程序还是相同的程序9_2,只不过在运行时动态链接到了英国版的库;

    程序不需要重新编译就能从新库中动态获取代码;

    有了动态库,就能随时替换代码;

 

 目录结构署名:

(P1)


 要点:

 -动态库在运行时链接程序;

 -用一个或多个目标文件创建动态库;

 -在一些机器上,需要使用-fPIC选项来编译目标文件;

 - -fPIC令目标代码位置无关;有些机器上可以省略它;

 - -shared编译选项可以动态创建动态库;

 -动态库在不同机器上名字不同;之所以不同,是因为不同操作系统有不同的优化策略;

 -如果把动态库保存在标准目录中,会更简单;否则,就需要设置PATH变量或LD_LIBRARY_PATH变量;

 -编译器在编译动态库时会在文件中保存库名;重命名文件是无效的,需要重新编译它;

 -使用静态库可以得到一个小而快的可执行程序,也方便copy复用;

 -使用动态库允许在运行时配置程序;

 

 C语言工具箱:

 -#include<>会查找/usr/include在内的标准目录;

 -ar命令会创建目标文件的存档;

 - -l<路径名>会链接标准目录下的文件;

 - -L<路径名>会在标准目录lib中添加目录;

 - -I<路径名>在标准include目录列表中添加目录;

 -gcc -shared把目标文件转化为动态库;

 -运行库在运行时链接;

 -库存档名形如libxxx.a;

 -库存档是动态链接的;

 -动态库的后缀名有.so、.dylib、.dll和.dll.a等;

 -动态库在不同的操作系统上有不同的名字;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值