library是什么意思

更新时间:2022-11-25 15:02:52 阅读: 评论:0


2022年11月25日发(作者:繁华的近义词和反义词)

使⽤sharedlibrary动态链接库

以前在Windows⾥⾯写C程序,总是⽤各种IDE,编译、链接之类的东西全不⽤操⼼,写好代码就好了。换到linux环境有⼏年了,写C程序

不再⽤IDE,⽽是⾃⼰⽤gcc/g++编译,不过之前的程序都不⼤,也没真正⽤到gcc/g++的各个选项。现在在学习cudac,有了⼀些可以公⽤

的⼯具代码,想要封装成库⽂件以共享,这才学会了使⽤sharedlibrary动态链接库。在这⾥总结⼀下,其实⾮常容易。

⾸先要对C的编译过程有个基本的了解。编译的第⼀步是预编译,处理各种预编译指令,各种带#号的指令如#include,#define都是预编译命

令。#define是简单的字符串替换,⽽#include则是对⽂件的复制粘贴。在C中,将类型和函数定义与实现分开放在.c/.cpp和.h⽂件中,实现

了⼀层封装,这样就可以在不改变定义的情况下改变函数或类型的实现。另⼀⽅⾯,将实现与定义拆分开来使得其他⽂件调⽤它们变得更容

易,只需要导⼊函数和类型的声明,⽽不需要加载整个实现的⽂件。

注意编译时只编译.c/.cpp⽂件,.h⽂件不⽤编译,这些⽂件仅仅是⽤来做⽂本上的复制粘贴的。每⼀个c/cpp⽂件的编译过程是将c代码转变

成汇编代码或者机器码的过程,编译的结果是⽣成object⽂件,在linux中后缀⼀般为.o。编译的过程中可能⽤到其他没有在这个⽂件中定义

的函数或者类型,只要⽂件中包含了函数或者类型的声明,编译还是能够正常进⾏。.h⽂件的意义这时候就体现出来了。

接下来的链接这个步骤就是来解决⽂件间的互相依赖问题的。这个过程⼤概就是把⼀部分object⽂件中缺失的符号定义在另⼀部分object⽂

件中找到,再通过某种⽅式把不同的部分拼接起来。链接的结果是⽣成可执⾏⽂件,或者是库⽂件。这些⽂件可以直接运⾏或者被调⽤,因

为所有的相互依赖问题都已经得到了解决。可执⾏⽂件都有⼀个main函数作为执⾏⼊⼝,库⽂件不需要main函数,只被别的代码调⽤。链

接有两种类型,⼀种是静态链接,直接将所⽤到的各个部分拼接起来作为所得到的最终⽂件。另⼀种是动态链接,不是直接的进⾏拼接,最

终得到的⽂件仍然不是完整的程序,⽽是保留了接⼝。动态链接所得到的可执⾏⽂件在执⾏的时候按照需要动态的加载⽤到的部分,与此不

同的静态链接没有这个动态加载的过程,因为所有需要的部分已经包含在可执⾏⽂件⾥⾯了。这⾥可以看到动态链接⽣成的⽂件往往会⼩⼀

些,因为可重⽤的部分已经独⽴出来作为库⽂件了,这也是构建⼤型软件所必须的。Windows中的.dll⽂件,linux中的.so⽂件都是动态链接

库⽂件,可以被其他程序在运⾏时动态的加载进来。

现在介绍⼀些gcc/g++的基本使⽤和⼀些选项。对于简单的⼩程序,直接gcc/g++加上所有的c/cpp⽂件不需要任何选项就可以得到可执⾏⽂

件了,默认⽣成的⽂件叫做。选项-o可以⽤来指定⽣成的⽂件名。对于复杂⼀些的程序,⼀般要向上⾯介绍的那样分步进⾏编译和链

接。编译的选项是-c,有了这个选项之后就只进⾏编译,并⽣成object⽂件,如前所述,这些⽂件中可以含有没有定义的部分。不加-c选

项,gcc/g++就会⾃动进⾏这个步骤。注意当编译含有没有定义的函数或者类型的⽂件时-c这个选项是必须的。

这⾥给⼀个简单的例⼦,有三个⽂件,foo.h和foo.c定义实现了⼀个简单的函数,然后test.c调⽤这个函数。

//foo.h

#ifndef__FOO_H__

#define__FOO_H__

voidprint_msg();

#endif

//foo.c

#include

#include"foo.h"

voidprint_msg()

{

printf("Hellon");

}

//test.c

#include"foo.h"

intmain()

{

print_msg();

return0;

}

现在假定这三个⽂件都在同⼀个⽂件夹下。⾸先,编译foo.c,

gcc-cfoo.c-ofoo.o

注意编译的命令中不需要.h⽂件,这些.h⽂件会在预编译时⾃动粘贴进来。这⼀步完成之后⽣成object⽂件foo.o。其内容是⼀个print_msg()

函数。

然后编译链接test,

.o-otest

这⼀步会编译test.c⽂件,然后将foo.o链接进来,⽣成最终的可执⾏⽂件test。这⼀步也可以拆成两步,先⽤-c选项编译test.c,然后再链接⽣

成test。这⼀步种的链接过程属于静态链接,因为foo.o的内容会被放到最终⽣成的test可执⾏⽂件中。

转换成使⽤动态链接,编译foo.c的时候需要⽤到-shared和-fPIC两个额外的选项。-shared选项指定编译器⽣成sharedlibrary,-fPIC选项中

PIC是Position-IndependentCode的缩写,意思是⽣成的机器代码不依赖于某个绝对内存地址。因为sharedlibrary可能被多个程序使⽤,若

是⽤绝对地址的话不同的程序使⽤过程中很可能会产⽣冲突,所以使⽤-fPIC选项,使得⽣成的代码适合作为sharedlibrary使⽤。gcc/g++有

不少选项是-f开头的,这些选项常常是⼀些⼩功能的开关。编译foo.c的命令如下:

注意⽣成的⽂件后缀⼀般为so,⽽且sharedlibrary⼀般都以lib开头。

编译链接test的时候,需要⽤到,只需要把foo.o换成,

-otest

这时⽣成的test就是依赖于的了。运⾏test,可以正常执⾏。但若把移到另⼀个⽂件夹,test就⽆法正常执⾏

了,因为找不到libfoo这个库。与此不同的是,对于静态链接的程序,⽆论把foo.o移到哪⾥,编译好的test⽂件都可以正常执⾏。

现在考虑更通⽤的情况,test.c与foo.c和foo.h分别放在两个不同的路径中,设test.c的路径为/u/dev/test/test.c,foo.c(h)的路径

为/u/dev/foo/foo.c(h)。通常库和调⽤库的程序是不同时写的,放在不同的地⽅也是最通常的情况。⾸先到/u/dev/foo/⾥⾯去编译,所

⽤的命令同上⾯的⼀样。然后回到/u/dev/test/,来编译test。⾸先第⼀个问题就是到哪⾥去找foo.h?这需要⽤到-I选项,它指定了include⽂

件的路径。于是我们可以⽤这个命令

gcc-I/u/dev/foo/test.c/u/dev/foo/-otest

往往库⽂件会集中放在⼀个路径中,要指定所有库⽂件的完整路径⾮常费事,gcc提供了⼀个-L选项,⽤来指定库⽂件的路径,结合-l(⼩写

L)来使⽤,指定⽤哪⼀些库⽂件。⽤在这个例⼦上,完整的命令就变为

gcc-I/u/dev/foo-L/u/dev/foo-lfoomain.c-otest

-l选项后跟的是库⽂件的名字,注意gcc⾃动把⽂件名的lib前缀去掉了,所以-lfoo实际上⽤的是,如果⽂件名是则反⽽找不

到。若/u/dev/foo中有多个库⽂件需要⽤,那么使⽤-lfoo1-lfoo2就⽐⽤完整路径简单多了。据说在某些新版本的gcc中,-l选项要放到-o选项

的后⾯,按照依赖关系来排,不过我还暂时没有遇到这个问题。

接下来运⾏test⽂件时仍然不能正常运⾏,这是因为找不到foo库⽂件的缘故。⼀个(可能不太好但可以⽤的)解决的办法是使⽤

LD_LIBRARY_PATH环境变量,这个环境变量指定了sharedlibrary的路径。在运⾏test之前,更新这个环境变量

exportLD_LIBRARY_PATH=/u/dev/foo:$LD_LIBRARY_PATH

然后再执⾏test,就没有问题了。

在通常的开发过程中,编译好的.so⽂件⼀般放在名为lib的⽂件夹中,.h⽂件⼀般放在include⽂件夹中,linux系统本⾝也如此。在使⽤这些

库的时候,编译时⽤-I,-L和-l选项即可。将上述环境变量的更新放在.bashrc中,便可以省去每次运⾏前都指定库⽂件⽬录的⿇烦了。

本文发布于:2022-11-25 15:02:52,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/19189.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:alabama
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图