在粤嵌学了很多知识,我们把常用的公用函数放在一起做成一个函数库,可以供其他程序共同使用,函数库本质上说,是一个可执行代码的二进制形式,可被操作系统载入内存执行。linux下的库分为两种:静态库,后缀名为
.a和动态库后缀名为.so(共享库);
两者的区别是:
静态库和动态库被载入应用程序的时刻不同,调用静态库函数的应用程序在编译时,会把静态库的代码链接到目标代码中(也就是应用程序的代码中),因此程序所占内存比较大,以后运行此目标程序时不再需要静态库;而动态库在程序编译时不会链接到目标代码中,动态库只有在程序执行时,才被载入内存,因此目标程序所占空间小(如下图),但是目标程序每次运行还要依赖这个动态库,所以如果这个动态库不存在了,则程序无法运行。
那么,linux下是如何使用和创建自己的函数库呢?我们以一个sin.c函数计算为例:
#include
int main(void)
{
float value;
value = sin ( 3.14 / 2 );
printf("%f\n",value);
}
我们编译这个函数:
gcc -o sin sin.c
出现以下错误信息:
sin.c: In function 'main':
sin.c:5: warning: incompatible implicit declaration of built-in
function 'sin'
/tmp/cciDlilg.o(.text+0x2c): In function `main':
sin.c: undefined reference to `sin'
collect2: ld returned 1 exit status
‘undefined reference to sin’,说的是没有 sin 的相关定义参考值!这是因为 C 语言里面的 sin
函示是写在 libm.so
这个函式库中,而我们并没有在原始码里面加入相关的说明,所以当然就需要在编译与连结的时候将这个函式库给他连结进执行档里面,所以可以这样做:
gcc -o sin sin.c -L/usr/lib -lm
"-L"是指定libm.so库所在的路径(linux函数库在/uer/lib中),-l是指定库函数的名字,
“m ” :则是 libm.so 这个函式库,其中, lib 与副档名(.a 或 .so)不需要写。
太棒了!终于编译成功了。现在看看如何建立自己的库!所有库的都是由.o文件生成的,我们再看一个简单的例子,先写出以下三个程序:程序hello.c:
#include
void hello(const char *name)
{
printf("hello:%s!\n",name);
}
程序hello.h:
#ifdef HELLO_H
#define HELLO_H
void hello(const char *name)
#endif
程序main.c:
#include
int main()
{
hello("you are a good boy!");
return 0;
}
hello.c是我们要做的函数库的源文件,里面包含库函数hello(); hello.h是该库函数的头文件,main.c为测试这个函数库的程序; 如果我们这样编译main.c程序, gcc -o main
main.c 会出现以下错误: main.c:1:18:
error: hello.h: 没有那个文件或目录提示找不到头文件,因为hello.h是我们自己写的头文件,标准c函数库里面没有这个头文件,我们在编译时加一个参数-I.,指明hello.h在当前目录路径下(如果你放在其他路径下,就改为:-I/头文件路径名):
gcc -o main main.c -I.
不要开心太早,这时还会有错误:
/tmp/ccr2NRXk.o: In function `main':
main.c:(.text+0x11): undefined reference to `hello'
collect2: ld returned 1 exit status
这个错误是不是跟上面编译sin函数时出现的“sin.c:
undefined reference to `sin”是不是很像,没错,找不到库函数hello;因为缺省的/uer/lib里面没有这个库函数,这时候我们就要自己做一个函数库!先做一个静态库:1:将hello.c编译成hello.o文件,无论静态库还是动态库都由.o文件生成:
gcc -c hello.c
2:由.o文件来创建静态库:静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。
ar crv libmyhello.a
hello.o这是如果你看到libmyhello.a文件,说明你的静态库已经创建成功了!现在我们看看如何使用这个静态库:在程序main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。
gcc -o main main.c -L. -lmyhello
./main
hello:you are a good boy!
运行成功,由于静态库是在编译的链接到目标程序中的,编译成功后就不依赖静态库,此时你可以删除静态库libmyhello.a,在运行以下程序验证上面的说法!我们再来看看如何创建一个动态库:
1:动态库的命名个静态库一样,也是在动态库名增加前缀lib,但其文件扩展名为
.so。我们用gcc创建一个动态库名为myhello的文件:
gcc -shared -fPCI -o libmyhello.so hello.o
2:现在使用刚创建成功的libmyhello.so:在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。
gcc -o main main.c -L. -lmyhello
./main
./main: error while loading shared libraries: libmyhello.so:
cannot open shared object file: No such file or directory
错
误提示找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态
库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。
# mv libmyhello.so /usr/lib
# ./main
Hello:you are a good boy!
成功。这也进一步说明了动态库在程序运行时是需要的。
一般情况下,我们为了保持/usr/liub库的原始性,我们不把自己创建的动态库放在目录/usr/lib中,而是在其他路径下自己建立一个目录,然后把我们自己的函数库集中放在里面,这是编译时就要修改环境变量LD_LIBRARY_PATH,指向自己创建的函数库的路径,在linux下可以用export命令来设置和查看这个环境变量;
另外:当静态库和动态库同名时,gcc将优先使用动态库,可以自己验证以下!