【www.shanpow.com--简历制作】
制作c库文件篇1:linux库文件的制作
一、为什么要使用库文件
我们在实际编程工作中肯定会遇到这种情况:有几个项目里有一些函数模块的功能相同,
实现代码也相同,也是我们所说的重复代码。比如,很多项目里都有一个用户验证的功能。
代码段如下:
//UserLogin.h文件,提供函数声明
int IsValidUser(char* username, int namelen);
//UserLogin.c文件,实现对用户信息的验证
int IsValidUser(char* username, int namelen)
{
int IsValid = 0;
/*下面是具体的处理代码,略去*/
return IsValid
}
如果每个项目都保存着这两个UserLogin.h和UserLogin.c文件,会有以下几个
弊端:
1、每个项目里都有重复的模块,造成代码重复。
2、代码的重用性不好,一旦IsValidUser的代码发生了变化,为了保持设计的一致性,
我们还要手工修改其他项目里的UserLogin.c文件,既费时又费力,还容易出错。
库文件就是对公共代码的一种组织形式。
为了解决上面两个弊端,就提出了用库文件存放公共代码的解决方案,其要点就是
把公共的(也就是可以被多次复用的)目标代码从项目中分离出来,统一存放到库文件中,
项目要用到这些代码的时候,在编译或者运行的时候从库文件中取得目标代码即可。库文件
又分两种:静态库和动态库。
二、静态库与动态库
如果程序是在编译时加载库文件的,就是使用了静态库。如果是在运行时加载目标代码,
就成为动态库。换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,
程序的体积会膨胀。如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找
库文件和函数体,程序的体积基本变化不大。
静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;
动态库则是“以时间换空间”,增加了运行时间,但减少了程序本身的体积。
下面我们就以实际例子来看看如何使用这两种库.
三、静态库的编写和使用
1、概述
静态库文件的扩展名一般为.a,其编写步骤很简单。
⑴编写函数代码
⑵编译生成各目标文件
⑶用ar文件对目标文件归档,生成静态库文件。
注意归档文件名必须以lib打头。
使用要点:
⑴在gcc 的-I参数后加上静态库头文件的路径。
⑵在gcc 的-L参数后加上库文件所在目录
⑶在gcc 的-l参数后加上库文件名,但是要去掉lib和.a扩展名。
比如库文件名是libtest.a 那么参数就是 -l test
2、编写最简单的静态库文件
编写如下两个文件,注意放在同一目录中
myalib.h //静态库头文件
myalib.c //静态库实现文件
//myalib.h 文件的内容
void test();
//myalib.c 文件的内容
#inlcude
void test()
{
printf("test\n");
}
3、制作库文件
⑴生成目标文件
gcc -c myalib.c
执行完后会生成一个myalib.o文件
⑵用ar命令归档,格式为ar -rc <生成的档案文件名> <.o文件名列表>
再次提醒,归档文件名一定要以lib打头, .a结尾。
ar -rc libtest.a myalib.o
-c create的意思 -r replace的意思,表示当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。 到此静态函数库创建完毕。
执行完后会生成一个libtest.a文件
4、使用库文件
⑴编写一个测试程序main.c,内容为
//main.c 测试静态库调用的程序
#include "myalib.h" //要把函数的头文件包含进来,否则编译时会报错
int main(int argc,char* argv[])
{
test();
return 0;
}
⑵编译目标文件,注意要把静态库头文件的路径加到-I参数里面
gcc -I /root/exercise -o main.o -c main.c
现在生成了一个main.o文件
⑶生成可执行文件,注意要把静态库文件的路径加到-L参数里面,
把库文件名(去掉打头的lib和结尾的.a)加到-l参数后面。如下面所示
gcc -o main -L/root/exercise main.o -ltest
此时就会生成一个名为main的可执行文件
另外,注意- l参数好象应该加到输入文件名的后面,否则会报错。
比如gcc -o main -L/root/exercise -ltest main.o就会提示
main.o(.text+0x11): In function `main"":
: undefined reference to `test""
collect2: ld returned 1 exit status
原因:(摘自 man gcc )It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded. ⑷执行可执行文件查看效果
执行./main, 输出
test
说明执行成功。
四、动态库的编写
1、概述
动态库一般以.so结尾,就是shared object的意思.
其基本生成步骤为
⑴编写函数代码
⑵编译生成动态库文件,要加上 -shared 和 -fpic 选项 ,
库文件名以lib开头, 以.so 结尾。
使用方式分为两种: 隐式调用和显示调用
隐式调用类似于静态库的使用,但需修改动态链接库的配置文件/etc/ld.so.conf;
显示调用则是在主程序里使用dlopen、dlsym、dlerror、dlclose等系统函数。
具体的调用方式会在 "五、动态库的调用" 中详细说明.
2、编写最简单的动态库文件
为了便于对照, 我们仍然采用静态库中的文件做例子.
编写如下两个文件,注意放在同一目录中
myalib.h //静态库头文件
myalib.c //静态库实现文件
//myalib.h 文件的内容
void test();
//myalib.c 文件的内容
#inlcude
void test()
{
printf("test\n");
}
3、编译生成动态库 ,库文件名以lib开头, 以.so 结尾。
gcc -fpic -shared -o libtest.so myalib.c
此时就生成一个libtest.so文件
五、动态库的隐式调用
隐式调用的含义是代码里不出现库文件名,就是说这个代码和
调用静态库的代码是类似的。
1、编写测试文件
//main.c 测试动态库隐式调用的程序
#include "myalib.h" //要把函数的头文件包含进来,否则编译时会报错
int main(int argc,char* argv[])
{
test();
return 0;
}
2、 编译测试程序,与静态库类似,要把头文件的路径加到-I参数里面
gcc -I /root/exercise -o main.o -c main.c
现在生成了一个main.o文件
3、连接生成测试程序
gcc -o main -L/root/exercise main.o -ltest
现在生成了一个main文件
4、执行测试程序
./main
此时出现提示
./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory。
这个原因就是程序运行时并不知道动态库所在的路径,因此自然找不到。
解决这个问题的办法有三种。见下节
六、使动态库被系统共享的三种办法
(再次说明: 本节参考了计算机世界网雨亦奇的文章"LINUX动态链接库高级应用"
地址http://www.ccw.com.cn/htm/center/prog/02_3_13_3_2.asp)
(1)拷贝动态链接库到系统共享目录下,或在系统共享目录下为该动态链接库
建立连接(硬连接或符号连接均可,常用符号连接).这里说的系统共享目录,
指的是LINUX动态链接库存放的目录,包括
/lib,/usr/lib以及/etc/ld.so.conf文件内所列的一系列目录.
实例:执行
# cp libtest.so /lib
# ldconfig
或:
# ln -s `pwd`/libtest.so /lib
# ldconfig
注意pwd前后有两个反引号`,其目的是取得pwd命令的输出,即当前目录.
此时再执行main,即可成功.
(2)将动态链接库所在目录名追加到动态链接库配置文件/etc/ld.so.conf中.
# pwd >> /etc/ld.so.conf
# ldconfig
此时再执行main,即可成功.
(3)利用动态链接库管理命令ldconfig,强制其搜索指定目录,并更新缓存文件,便于动态装入.
# ldconfig `pwd`
此时再执行main,即可成功.
要注意,第三种方法虽然有效,但效果是暂时的,供程序测试还可以,一旦再度运行ldconfig,
则缓存文件内容可能改变,所需的动态链接库可能不被系统共享了.
而且无论哪种办法,其实质都是用ldconfig命令把动态库文件
所在路径加入到系统库列表中,(前两种永久,第三种临时)
补充:ldconfig命令的作用(见最后说明)
七、动态库的显式调用
显式调用的含义是代码出现库文件名,用户需要自己去
打开和管理库文件。其要点为:
⑴把dlfcn.h系统头文件包含进来
⑵用dlopen函数打开库文件,并指定打开方式
dlopen的的第一个参数为共享库的名称,将会在下面位置查找指定的共享库。
①环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。
②文件/etc/ld.so.cache中找到的库的列表,由ldconfig命令刷新。
③目录usr/lib。
④目录/lib。
⑤当前目录。
第二个参数为打开共享库的方式。有两个取值
①RTLD_NOW:将共享库中的所有函数加载到内存
②RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数
⑶用dlerror()函数测试是否打开成功,并进行错误处理;
⑷用dlsym获得函数地址,存放在一个函数指针中
⑸用获得的函数指针进行函数调用。
⑹程序结束时用dlclose关闭打开的动态库,防止资源泄露。
⑺用ldconfig工具把动态库的路径加到系统库列表中
1、编写测试文件
//main.c 测试动态库显式调用的程序
#include //用于动态库管理的系统头文件
#include "myalib.h" //要把函数的头文件包含进来,否则编译时会报错
int main(int argc,char* argv[])
{
//声明对应的函数的函数指针
void (*pTest)();
//加载动态库
void *pdlHandle = dlopen("libtest.so", RTLD_LAZY);
//错误处理
if(pdlHandle == NULL ) {
printf("Failed load library\n");
return -1;
}
char* pszErr = dlerror();
if(pszErr != NULL)
{
printf("%s\n", pszErr);
return -1;
}
//获取函数的地址
pTest = dlsym(pdlHandle, "test");
pszErr = dlerror();
if(pszErr != NULL)
{
printf("%s\n", pszErr);
dlclose(pdlHandle);
return -1;
}
//实现函数调用
(*pTest)();
//程序结束时关闭动态库
dlclose(pdlHandle);
return 0;
}
2、编译测试文件 使用-ldl选项指明生成的对象模块需要使用共享库
gcc -o main -ldl main.c
执行完后就生成了一个main文件
3、执行测试程序
执行 ./main
输出
test
说明成功。
六、使用动态库时应注意的其他问题
1、无论是动态库的显式调用还是隐式调用,都需要用
ldconfig工具将动态库的路径加到系统库列表中,否则运行时会出错。
2、可以用ldd命令检查程序都使用到哪些共享库
ldd命令行用法如下:
ldd [--version] [-v|--verbose] [-d|--data-relocs] [-r|--function-relocs] [--help] FILE...
各选项说明如下:
(1) --version : 此选项用于打印出ldd的版本号.
(2) -v 或 --verbose : 此选项指示ldd输出关于所依赖的动态链接库的尽可能详细的信息.
(3) -d 或 --data-relocs : 此选项执行重定位,并且显示不存在的函数.
(4) -r 或 --function-relocs : 此选项执行数据对象与函数的重定位,同时报告不存在的对象.
(5) --help : 此选项用于打印出ldd的帮助信息.
我们一般用-v选项.
现在看几个实例
⑴用静态库连接时的结果
#ldd main
libc.so.6 => /lib/tls/libc.so.6 (0xb74ad000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)
可见使用静态库时,由于库已经被编译成程序的一部分,因此ldd的输出中就只有用到的
系统库。
⑵用动态库隐式连接时的结果
libtest.so => /root/exercise/libtest.so (0xb75e2000)
libc.so.6 => /lib/tls/libc.so.6 (0xb74ab000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)
可见隐式使用动态库时,所有用到的动态库(包括系统和用户的)都会被显示出来。
⑶动态库显式连接时的结果
ldd main
libdl.so.2 => /lib/libdl.so.2 (0xb75e1000)
libc.so.6 => /lib/tls/libc.so.6 (0xb74aa000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)
可见显式使用动态库时,程序中不再保存运行时打开动态库的信息,只保留用到的系统库的信息.
这个与使用静态库时的输出是类似的.
七.补充 :
ldconfig是一个动态链接库管理命令
为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig
ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为 /etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表.
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.
ldconfig命令行用法如下:
ldconfig [-v|--verbose] [-n] [-N] [-X] [-f CONF] [-C CACHE] [-r ROOT] [-l] [-p|--print-cache]
[-c FORMAT] [--format=FORMAT] [-V] [-?|--help|--usage] path...
ldconfig可用的选项说明如下:
(1) -v或--verbose : 用此选项时,ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字.
(2) -n : 用此选项时,ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib,/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录.
(3) -N : 此选项指示ldconfig不重建缓存文件(/etc/ld.so.cache).若未用-X选项,ldconfig照常更新文件的连接.
(4) -X : 此选项指示ldconfig不更新文件的连接.若未用-N选项,则缓存文件正常更新.
(5) -f CONF : 此选项指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf.
(6) -C CACHE : 此选项指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,此文件存放已排好序的可共享的动态链接库的列表.
(7) -r ROOT : 此选项改变应用程序的根目录为ROOT(是调用chroot函数实现的).选择此项时,系统默认的配置文件 /etc/ld.so.conf,实际对应的为 ROOT/etc/ld.so.conf.如用-r /usr/zzz时,打开配置文件 /etc/ld.so.conf时,实际打开的是/usr/zzz/etc/ld.so.conf文件.用此选项,可以大大增加动态链接库管理的灵活性.
(8) -l : 通常情况下,ldconfig搜索动态链接库时将自动建立动态链接库的连接.选择此项时,将进入专家模式,需要手工设置连接.一般用户不用此项.
(9) -p或--print-cache : 此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名字.
(10) -c FORMAT 或 --format=FORMAT : 此选项用于指定缓存文件所使用的格式,共有三种: ld(老格式),new(新格式)和compat(兼容格式,此为默认格式).
(11) -V : 此选项打印出ldconfig的版本信息,而后退出.
(12) -? 或 --help 或 --usage : 这三个选项作用相同,都是让ldconfig打印出其帮助信息,而后退出.
linux下的共享库机制采用了类似于高速缓存的机制,将库信息保存在/etc/ld.so.cache里边。
程序连接的时候首先从这个文件里边查找,然后再到ld.so.conf的路径里边去详细找。
这就是为什么修改了ld.so.conf要重新运行一下ldconfig的原因
补充一点,ldconfig在/sbin里面。
制作c库文件篇2:库文件
本文主要解决以下几个问题 1 为什么要使用库? 2 库的分类 3 创建自己的库 或许大家对自己初学linux时的情形仍记忆尤新吧。如果没有一个能较好的解决依赖关系的包管理器,在linux下安装软件将是一件及其痛苦的工作。你装a包时,可能会提示你要先装b包,当你费尽心力找到b包时,可能又会提示你要先安装c包。我就曾被这样的事搞的焦头烂额,至今一提起rpm仍心有余悸,头皮发麻。说是一朝被蛇咬,十年怕井绳怕也不为过。 linux下之所以有这许多的依赖关系,其中一个开发原则真是功不可没。这个原则就是:尽量不重复做别人已经做过的事。换句话说就是尽量充分利用别人的劳动成果。 这就涉及到如何有效的进行代码复用。 1 为什么要使用库? 关于代码复用的途径,一般有两种。 粘贴复制 这是最没有技术含量的一种方案。如果代码小,则工作量还可以忍受,如果代码很庞大,则此法不可取。即便有人原意这样做,但谁又能保证所有的代码都可得到呢? 而库的出现很好的解决了这个问题。 库,是一种封装机制,简单说把所有的源代码编译成目标代码后打成的包。 那么用户怎么能知道这个库提供什么样的接口呢?难道要用nm等工具逐个扫描? 不用担心,库的开发者早以把一切都做好了。除了包含目标代码的库外,一般还会提供一系列的头文件,头文件中就包含了库的接口。为了让方便用户,再加上一个使用说明就差不多完美了。 2 库的分类 2.1 库的分类 根据链接时期的不同,库又有静态库和动态库之分。 静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。 有别于静态库,动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。(TODO:链接动态库时链接阶段到底做了什么) 2.2 静态库和动态库的比较 链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题。 首先就是系统空间被浪费了。这是显而易见的,想象一下,如果多个程序链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪费系统空间。 再者,人非圣贤,即使是精心调试的库,也难免会有错。一旦发现了库中有bug,挽救起来就比较麻烦了。必须一一把链接该库的程序找出来,然后重新编译。 而动态库的出现正弥补了静态库的以上弊端。因为动态库是在程序运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间。如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。 那么,是不是静态库就一无是处了呢? 答曰:非也非也。不是有句话么:存在即是合理。静态库既然没有湮没在滔滔的历史长河中,就必然有它的用武之地。想象一下这样的情况:如果你用libpcap库编了一个程序,要给被人运行,而他的系统上没有装pcap库,该怎么解决呢?最简单的办法就是编译该程序时把所有要链接的库都链接它们的静态库,这样,就可以在别人的系统上直接运行该程序了。 所谓有得必有失,正因为动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会打折扣。然而瑕不掩瑜,动态库的不足相对于它带来的好处在现今硬件下简直是微不足道的,所以链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。
《程序员的自我修养—链接、装载与库》的解释显然不完整,完整的应该是,-static参数是指定使用静态库,编译目标文件。
2.3 如何判断一个程序有没有链接动态库? 答案是用file实用程序。 file程序是用来判断文件类型的,在file命令下,所有文件都会原形毕露的。
file命令,可以看到,如,显示dynamically linked (uses shared libs),statically linked等。
顺便说一个技巧。有时在windows下用浏览器下载tar.gz或tar.bz2文件,后缀名会变成奇怪的tar.tar,到linux有些新手就不知怎么解压了。但linux下的文件类型并不受文件后缀名的影响,所以我们可以先用命令file xxx.tar.tar看一下文件类型,然后用tar加适当的参数解压。 另外,还可以借助程序ldd实用程序来判断。 ldd是用来打印目标程序(由命令行参数指定)所链接的所有动态库的信息的,如果目标程序没有链接动态库,则打印“not a dynamic executable”,ldd的用法请参考manpage。 3 创建自己的库 3.1 创建动态库 创建文件hello.c,内容如下: #include void hello(void) { printf("Hello World\n"); } 用命令gcc -shared hello.c -o libhello.so编译为动态库。可以看到,当前目录下多了一个文件libhello.so。 [leo@leo test]$ file libhello.so libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped 看到了吧,文件类型是shared object了。 再编辑一个测试文件test.c,内容如下: int main() { hello(); return 0; } 这下可以编译了:) [leo@leo test]$ gcc test.c /tmp/ccm7w6Mn.o: In function `main": test.c:(.text+0x1d): undefined reference to `hello" collect2: ld returned 1 exit status 链接时gcc找不到hello函数,编译失败:(。原因是hello在我们自己创建的库中,如果gcc能找到那才教见鬼呢!ok,再接再厉。 [leo@leo test]$ gcc test.c -lhello /usr/lib/gcc/i686-pc-linux-gnu/4.0.0/../../../../i686-pc-linux-gnu/bin/ld: cannot find -lhello collect2: ld returned 1 exit status [leo@leo test]$ gcc test.c -lhello -L. [leo@leo test]$ 第一次编译直接编译,gcc默认会链接标准c库,但符号名hello解析不出来,故连接阶段通不过了。 现在用gcc test.c -lhello -L.已经编译成功了,默认输出为a.out。现在来试着运行一下: [leo@leo test]$ ./a.out ./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory 咦,怎么回事?原来虽然链接时链接器(dynamic linker)找到了动态库libhello.so,但动态加载器(dynamic loader, 一般是/lib/ld-linux.so.2)却没找到。再来看看ldd的输出: [leo@leo test]$ ldd a.out linux-gate.so.1 => (0xffffe000) libhello.so => not found libc.so.6 => /lib/libc.so.6 (0x40034000) /lib/ld-linux.so.2 (0x40000000) 果然如此,看到没有,libhello.so => not found。 linux为我们提供了两种解决方法: 1.可以把当前路径加入/etc/ld.so.conf中然后运行ldconfig,或者以当前路径为参数运行ldconfig(要有root权限才行)。 2.把当前路径加入环境变量LD_LIBRARY_PATH中 当然,如果你觉得不会引起混乱的话,可以直接把该库拷入/lib,/usr/lib/等位置(无可避免,这样做也要有权限),这样链接器和加载器就都可以准确的找到该库了。 我们采用第二种方法: [leo@leo test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH [leo@leo test]$ ldd a.out linux-gate.so.1 => (0xffffe000) libhello.so => ./libhello.so (0x4001f000) libc.so.6 => /lib/libc.so.6 (0x40036000) /lib/ld-linux.so.2 (0x40000000) 哈哈,这下ld-linux.so.2就可以找到libhello.so这个库了。 现在可以直接运行了: [leo@leo test]$ ./a.out Hello World 3.2 创建静态库 仍使用刚才的hello.c和test.c。 第一步,生成目标文件。 [leo@leo test]$ gcc -c hello.c [leo@leo test]$ ls hello.o -l -rw-r--r-- 1 leo users 840 5月 6 12:48 hello.o 第二步,把目标文件归档。 [leo@leo test]$ ar r libhello.a hello.o ar: creating libhello.a OK,libhello.a就是我们所创建的静态库了,简单吧:) [leo@leo test]$ file libhello.a libhello.a: current ar archive 下面一行命令就是教你如何在程序中链接静态库的: [leo@leo test]$ gcc test.c -lhello -L. -static -o hello.static 我们来用file命令比较一下用动态库和静态库链接的程序的区别: [leo@leo test]$ gcc test.c -lhello -L. -o hello.dynamic 正如前面所说,链接器默认会链接动态库(这里是libhello.so),所以只要把上个命令中的-static参数去掉就可以了。 用file实用程序验证一下是否按我们的要求生成了可执行文件: [leo@leo test]$ file hello.static hello.dynamic hello.static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, statically linked, not stripped hello.dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, dynamically linked (uses shared libs), not stripped 不妨顺便练习一下ldd的用法: [leo@leo test]$ ldd hello.static hello.dynamic hello.static: not a dynamic executable hello.dynamic: linux-gate.so.1 => (0xffffe000) libhello.so => ./libhello.so (0x4001f000) libc.so.6 => /lib/libc.so.6 (0x40034000) /lib/ld-linux.so.2 (0x40000000) OK,看来没有问题,那就比较一下大小先: [leo@leo test]$ ls -l hello.[ds]* -rwxr-xr-x 1 leo users 5911 5月 6 12:54 hello.dynamic -rwxr-xr-x 1 leo users 628182 5月 6 12:54 hello.static 看到区别了吧,链接静态库的目标程序和链接动态库的程序比起来简直就是一个庞然大物! 这么小的程序,很难看出执行时间的差别,不过为了完整起见,还是看一下time的输出吧: [leo@leo test]$ time ./hello.static Hello World real 0m0.001s user 0m0.000s sys 0m0.001s [leo@leo test]$ time ./hello.dynamic Hello World real 0m0.001s user 0m0.000s sys 0m0.001s 如果程序比较大的话,应该效果会很明显的。
一些参考文章:
http://www.360doc.com/content/10/0619/14/1795182_33980847.shtml
http://www.360doc.com/content/10/0123/14/79031_14221583.shtml
http://www.360doc.com/content/09/1224/21/510771_11904716.shtml
摘要:使用gcc编译器就可以将库与自己开发的程序连接起来,例如:libc.so.5中包含了标准的输入输出函数,当连接程序进行目标代码连接时会自动搜索该程序并将其连接到生成的可执行文件中。标准的输入输出库中包含了许多基本的输入输出函数,如printf函数等。也可以连接其它的一些系统函数库,如数学库等,但与libc.so.5不同,大部分其它的系统库需要在命令行中显式指定所用的库名。
http://www.360doc.com/content/08/1016/18/36491_1774689.shtml
现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供sayhello()这个函数 /* hello.h */ void sayhello(); 另外还有一些说明文档。
这一个典型的程序开发包结构 与动态库连接 linux默认的就是与动态库连接
nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多
http://www.360doc.com/content/09/1224/21/510771_11905540.shtml
http://www.360doc.com/content/09/0609/18/109143_3831910.shtml (windows下)
http://www.360doc.com/content/09/0618/17/150654_3946451.shtml(简洁)
http://www.360doc.com/content/10/0123/14/79031_14221546.shtml
http://www.fmddlmyy.cn/text3.html
C Run-Time Libraries有静态库版本,也有动态链接库版本;有单线程版本,也有多线程版本;还有调试和非调试版本。动态链接库版本:/MD Multithreaded DLL 使用导入库MSVCRT.LIB/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB
静态库版本:/ML Single-Threaded 使用静态库LIBC.LIB /MLd Debug Single-Threaded 使用静态库LIBCD.LIB/MT Multithreaded 使用静态库LIBCMT.LIB/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB
MFC的库可以静态链接,也可以动态链接。静态库和动态库又有Debug和Release,ANSI和Unicode版本之分。
http://blog.kangkang.org/index.php/archives/17
http://www.google.com.hk/url?sa=t&source=web&cd=6&ved=0CGsQFjAF&url=http%3A%2F%2Fread.pudn.com%2Fdownloads9%2Fdoc%2F36169%2Funix%25E7%25AE%25A1%25E7%2590%2586%25E5%25A4%25A7%25E5%2585%25A8%2FUNIX%25E7%25B3%25BB%25E7%25BB%259F%25E5%25BC%2580%25E5%258F%2591%25EF%25BC%258D%25E9%2593%25BE%25E6%258E%25A5%25E5%25A4%2584%25E7%2590%2586%25EF%25BC%25881%25EF%25BC%2589.doc&ei=48ubTsH4MuGUiQf_pvmrAg&usg=AFQjCNEE4LX8cU2LdVpQ9WQ9bYT2GT31Dw&sig2=2pHheAmlvx2ArpZDQmvJfQ
标准的C函数库有动态和静态两个不同的版本,其文件名分别是libc.so和libc.a,分别用于动态链接和静态链接。缺省情况下,链接程序将对标准C库函数调用进行动态链接(使用libc.so),也即所调用的函数将在运行时与程序相链接
制作c库文件篇3:linux 下C语言编程库文件处理与Makefile编写
做开发快3年了,在linux下编译安装软件算是家常便饭了。就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题。看来还是像vs、codeblocks这样的ide把人弄蠢了。便下定决心一定要好好学习下如何在linux下纯手工gcc编译c项目。今天学了2点,一个是库文件处理,另一个是makefile编写。
学习的系统是centos6.6,编译升级的gcc4.8.2,明天写个博客总结下这回gcc安装的过程,每次都能学到些东西。
gcc的编译过程
首先需要清楚gcc编译做了些什么
源文件----预处理---->预处理文件(*.i)----编译---->汇编文件(*.s)----汇编编译---->目标文件(*.o)----链接---->可执行文件
基本与windows下的类似。通过给gcc加一些选项,可以控制编译工作进行到指定的阶段,下面试常用的一些gcc编译选项
-c 编译,汇编源文件,不链接,得到*.o文件
-S 只编译,不汇编,得到*.s文件
-E 预处理文件,得到*.E文件
-o [dis] [src] 将src文件编译成可执行文件dis
-Idir 指定include包含文件搜索路径
-g 生成具有调试信息,如果不加这个选项,*.o文件中不会生成.debug段,在调试时将不能查看打印变量值
学习过c和c++的都知道,c或c++程序存在各种各样的库文件,就是已经编译好的包含数据和执行代码的二进制文件。windows就是dll文件,linux下有.a和.so文件。如果需要使用这些库文件,在ide环境下勾个选项就把事给办了,在手工编译的情况下就麻烦了点。
gcc创建和使用静态库
编写static_lib.c文件
创建静态库
1 gcc -c static_lib.c
2 ar rcs static_lib.a static_lib.o
上面的命令会在当前目录下生产 static_lib.a 静态库文件
使用链接静态库
编写 static_lib.h文件
编写main3.c文件,使用静态库中的方法
编译main3.c并链接静态库文件
执行
1 gcc main3.c -lstatic_lib.a -o app3
但却出现链接器ld找不到库的问题,把-l参数去掉就正常了
1 gcc main3.c static_lib.a -o app3
最后会生成可执行文件app3。静态库的特点是将库里的代码放到了执行文件里,如果修改了静态库的代码,要重新编译依赖它执行文件才能升级
gcc创建和使用动态库
动态库就是在有执行文件需要使用这个库时,动态加载到执行的库文件。
编写share_lib.c文件
创建动态库
因为需要与位置无关,所以需要使用-fPIC选项,gcc的选项有上千个,需要查询某个选项可以man gcc然后查找查看
1 gcc -shared -fPIC -o share_lib.so share_lib.c
生成的share_lib.so文件就是动态库文件,在使用这个库的程序使用时被动态加载,并没有被写入到别的执行文件中,所以当库文件修改,不需要去重新编译其他使用这个库的程序
使用动态库
share_lib.h文件声明函数
编写main4.c文件,include "share_lib.h" 文件
编译main4.c并链接动态库
1 gcc main4.c ./share_lib.so -o app4
生成的app4就是可执行文件.
编写makefile编译
将前面静态库的3个源文件main3.c,static_lib.c,static_lib.h放到一个目录下。
编写Makefile文件。
其中冒号左边表示目标文件或者命令,命令也叫伪目标。
执行make
执行make clean可以清除编译产生的文件
当然,这恐怕是最简单的makefile了,Makefile还有很多学的,我也在学习中,以后有收获会继续写博客记录,争取以后看大神写的makefile不要在略懂略懂了