您的位置:首页 > 运维架构 > Linux

静态链接库和动态链接库 (参考linux程序设计4th)

2011-07-09 19:46 225 查看
原文引用:http://blog.csdn.net/Andrew659/article/details/5726701

搭建Ubuntu C 环境

输入sudo apt-get install build-essential 来安装各种必须软件和头文件。

C源程序到最后的可执行代码需要经过:预处理,汇编,编译,链接过程,链接的过程就是把编译好的目标代码(.o文件)和其他目标代码(比如几个目标文件链接成一个可执行程序)还有库文件链接起来组成一个可执行程序。
通常我们写C程序的第一句代码都会是#include<stdio.h>,用以包含进来标准输入输出的头文件,头文件用以定义一些常量和声明一些函数,最简单的程序莫过于。
view plain#include<stdio.h>
int main()
{
printf("hello world!/n");
exit(0);
}
那么我们在编译的时候常用指令gcc -o HelloWorld HelloWorld.c来生成可执行程序HelloWolrd。编译器在处理过程中会搜索系统库文件(一般存储在/lib和/usr/lib中)以搜索到和stdio.h相对应的库文件,完成链接生成可执行程序。
所以我猜测,如果用户明白怎么链接的话,可以执行链接文件,甚至连头文件都不用包含进来。
实验过程:
part1:首先我不改变源文件,只是在链接的时候再指明下链接libc.a静态库文件
键入命令:gcc -o HelloWorld HelloWorld.c -lc (或者gcc -o HelloWorld HelloWorld.c /usr/lib/libc.a, -l形式的是简写,因为linux中静态库文件总是以lib开头,.a结尾,简写的时候搜索默认位置,如果不在默认为止,需要要指明路径,用-L参数。
-L使用:gcc -o HelloWorld HelloWorld -L/usr/lib -lc,试验中用gcc -o HelloWorld HelloWorld -L/usr/lib libc.a虽然表达同样的意思,但是被告知libc.a不存在,gcc -o HelloWorld HelloWorld.c /usr/lib/libc.a是可以的,可能-L和-l搭配的,而-L的单独出现也是可以的。实际上前者指明路径也是多此一举,只要保证能够找到库文件就ok)
注意:-L参数为编译器增加搜索库文件的路径,-lc这种写法在有共享库的情况下优先链接共享库,即动态库。
实验证明也是如此:
如果使用命令 gcc -o HelloWorld HelloWorld.c或者gcc -o HelloWorld HelloWorld.c (.L/usr/lib) -lc得到的可执行文件只有4841B,而如果使用命令gcc -o HelloWorld HelloWorld.c /usr/lib/libc.a可执行程序将会是617725B,因为其中包含了静态链接时加入的代码。

part2:将原文件中include一句删掉
运行命令 gcc -o HelloWorld HelloWorld.c /usr/lib/libc.a或者gcc -o HelloWorld HelloWorld.c -lc ,得到可执行文件,只是有个警告。

实验二:
我们都知道,在main函数中要调用某个自定义函数,一般有两种比较标准的做法:
1. 在main函数之前定义或者至少声明。
2. 在一个新头文件中声明,在一个新.c文件中定义,然后在主程序文件中include该头文件。如果将函数做成库文件的话以后就可以经常使用,如果不做成库文件其实头文件也不一定必要,只要链接的时候手动将该函数目标文件链上主程序即可。但是使用头文件和库是一种规范的做法,使编程简便。实验三种将做成库文件。
比如:自定义myPrint()函数

文件1:myPrint.c
view plain#include<stdio.h>
void myPrint()
{
printf("hello myPrint!/n");
}
文件3:program.c
view plainint main()
{
myPrint();
exit(0);
}
gcc -o program myPrint.c program.c照样有警告,但是能运行,实际上警告是由于exit(0)不兼容
所以,规范起见:使用include机制,要用到的函数,一定先把头文件包含进来。或者直接在main函数中定义。

实验三:静态库和动态库
实验目标:为经常使用的函数***库文件,以后再用的时候就直接include头文件即可。
part1:***静态链接库文件
文件一:lib.h (头文件,一般放在/usr/include目录,也会有部分在其他目录)
view plainvoid bill(void);
文件二:bill.c
view plain#include<stdio.h>
void bill()
{
printf("hello bill!/n");
}
文件三:main.c
view plain#include "lib.h"
int main()
{
bill();
exit(0);
}
注意:此时我们把lib.h和其他文件放在同一目录下,所以include用双引号,如果用尖括号会找不到lib.h(可能是搜索默认路径)
step1: 编译bill.c gcc -c bill.c
step2:归档 ar crv libfoo.a bill.o
step3: 编译main.c gcc -c main.c
step4: 链接 gcc -o main main.o libfoo.a (34可合并成:gcc -o main main.c libfoo.a)
也可用-l来访问自定义函数库,但是因为未保存在标准位置,所以必须使用-L来指明路径 gcc -o main main.o -L. -lfoo
此时可以看下其引用的动态库的情况:
ldd main



继续,如果把libfoo.a和lib.h放到默认路径下呢?应该就能够直接gcc -o main main.c

对linux程序来说,负责装载共享库并解析客户程序函数引用的程序(动态装载器)是ld.so,也可能是ld-linux.so.2或者ld-lsb.so.1。搜索共享库的其他位置可以在文件/etc/ld.so.conf中配置。


part2:***动态链接库文件

动态库的链接方式是这样的:它本身不在包含函数代码,而是在运行时刻引用可访问的共享代码,当编译好的程序被加载到内存中执行时,函数引用被解析,并且调用共享库,如果有必要共享库才被加载到内存(比如有时include很多没用的头文件)。
两大优点:1.节省宝贵的内存和磁盘空间。(想想为什么?)
2.共享库的更新独立于依赖它的应用程序。

***动态链接库:
step1: gcc -c -fPIC bill.c 在程序运行的时候,被调用的动态链接库函数被安置在内存的某个地方,所有调用它的程序都指向这一代码段,所以这些代码都要使用相对地址,在编译的时候使用PIC(position independent code)告诉编译器,这些代码是用来做动态链接库的。
step2:gcc -shared -o libfoo.so bill.o
也可把fpic参数放到下面即:gcc -shared -fPIC -o libfoo.so bill.o

下面就: gcc -o main main.c ./libfoo.so 注意:一定要指明libfoo.so的位置,否则链接可能会通过,但是会有运行时刻错误:找不到动态链接库。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: