GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结相关的知识,希望对你有一定的参考价值。

来自http://blog.csdn.net/benpaobagzb/article/details/51364005

GCC 编译使用动态链接库和静态链接库

 

 

1 库的分类

根据链接时期的不同,库又有静态库和动态库之分。

静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。

有别于静态库,动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。(TODO:链接动态库时链接阶段到底做了什么)

2 静态库和动态库的比较

链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题。

首先就是系统空间被浪费了。这是显而易见的,想象一下,如果多个程序链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪费系统空间。

再者,人非圣贤,即使是精心调试的库,也难免会有错。一旦发现了库中有bug,挽救起来就比较麻烦了。必须一一把链接该库的程序找出来,然后重新编译。

而动态库的出现正弥补了静态库的以上弊端。因为动态库是在程序运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间。如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。

那么,是不是静态库就一无是处了呢?

答曰:非也非也。不是有句话么:存在即是合理。静态库既然没有湮没在滔滔的历史长河中,就必然有它的用武之地。想象一下这样的情况:如果你用libpcap库编了一个程序,要给被人运行,而他的系统上没有装pcap库,该怎么解决呢?最简单的办法就是编译该程序时把所有要链接的库都链接它们的静态库,这样,就可以在别人的系统上直接运行该程序了。

所谓有得必有失,正因为动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会打折扣。然而瑕不掩瑜,动态库的不足相对于它带来的好处在现今硬件下简直是微不足道的,所以链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。

动态链接库

1. 创建动态链接库 

  1. #include<stdio.h>  
  2. void hello()  
  3. {  
  4.         printf("hello world/n");  
  5. }

用命令gcc -shared hello.c -o libhello.so编译为动态库。可以看到,当前目录下多了一个文件libhello.so。

 2. 再编辑一个测试文件test.c,内容如下

  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.         printf("call hello()");  
  5.         hello();  
  6. }  

 

编译 gcc test.c -lhello

-l 选项告诉编译器要使用hello这个库。奇怪的地方是动态库的名字是libhello.so,这里却使用hello.

 

但这样还不行,编译会出错。

In function `main‘:

 

test.c:(.text+0x1d): undefined reference to `hello‘

collect2: ld returned 1 exit status

 

这是因为hello这个库在我们自己的路径中,编译器找不到。

需要使用-L选项,告诉hello库的位置

 

gcc test.c -lhello -L. -o test

 

-L .告诉编译器在当前目录中查找库文件

 

3. 编译成功后执行./test, 仍然出错

说找不到库

 

有两种方法:

一、可以把当前路径加入 /etc/ld.so.conf中然后运行ldconfig,或者以当前路径为参数运行ldconfig(要有root权限才行)。

二、把当前路径加入环境变量LD_LIBRARY_PATH中

 当然,如果你觉得不会引起混乱的话,可以直接把该库拷入/lib,/usr/lib/等位置(无可避免,这样做也要有权限),这样链接器和加载器就都可以准确的找到该库了。

 

我们采用第二种方法:

 export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

 这样,再执行就成功了。

 

下面再讲讲静态链接库

仍使用刚才的hello.c和test.c。

 

1. gcc -c hello.c 注意这里没有使用-shared选项

2. 把目标文件归档    ar -r libhello.a hello.o

 

 

    程序 ar 配合参数 -r 创建一个新库 libhello.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。

3. 在程序中链接静态库

         gcc test.c -lhello -L. -static -o hello.static 

或者   gcc test.c libhello.a -L. -o hello.static

 

生成的hello.static就不再依赖libhello.a了


 

file和ldd命令

 

file程序是用来判断文件类型的,在file命令下,所有文件都会原形毕露的。

顺便说一个技巧。有时在 windows下用浏览器下载tar.gz或tar.bz2文件,后缀名会变成奇怪的tar.tar,到Linux有些新手就不知怎么解压了。但 Linux下的文件类型并不受文件后缀名的影响,所以我们可以先用命令file xxx.tar.tar看一下文件类型,然后用tar加适当的参数解压。


另外,还可以借助程序ldd实用程序来判断。

ldd是用来打印目标程序(由命令行参数指定)所链接的所有动态库的信息的,如果目标程序没有链接动态库,则打印“not a dynamic executable”,ldd的用法请参考manpage。


 

 
1,如何生成静态库
静态库只是一堆object对象的集合,使用ar命令可以将.o文件打包成.a静态库。
假设gcc已经生成了a.o, b.o, c.o,使用下面的命令即可生成libmylib.a
#ar rcs libmylib.a a.o b.o c.o
 
2,如何生成动态库
动态库的生成由gcc直接生成。
假设a.c, b.c两个文件,通过下面的命令可生成libmylib.so
#gcc a.c b.c -o libmylib.so --shared
 
3,如何使用库
gcc中关于库的参数有:
-L  指定搜寻库的目录
      如指定当前目录 gcc -L .
-l    指定要链接的库的名称
      加入库的名称是libmylib.a,则gcc -l mylib,即去头去尾。
--static  组织在链接时使用动态库
--shared 生成动态库
--static-libgcc  链接静态libgcc库
--shared-libgcc 链接动态libgcc库
 
可见对动态库和静态库的使用方法是一样的,同一个库如果同时存在动态库和静态库,优先链接动态库,除非使用--static强制使用静态库。

 


 

gcc -lXXX 如何选择静态库还是动态库?

库标准路径下存在libABC.a和libABC.so使用gcc -lABC如何选择连接静态连接库或者动态连接库?通过--hare --static选项?
 
答:如果在同一路径下面,并且两种库同名,这样会选择动态库。

 

gcc 混合连接动态库和静态库

 问:gcc 同时连接 静态库和动态库现在有 libmy.a & libmy.so两个库,其中的函数供main.cc调用要在可执行文件中同时连接这两个库gcc -g -lstdc++ -g -L. -lmy -l ./libmy.a -o test.exe main.cc // 报找不到libmy.a,可是在当前目录下已经有这个文件了gcc -g -lstdc++ -g -L. -l libmy.so -l ./libmy.a -o test.exe main.cc // 报找不到libmy.so,在当前目录下也有这个文件用了 -static 选线,则报动态库中的函数没定义请问大家有什么招不?感激
 
答:记得静态库混合动态库要加特殊指令的,你可以试试这样:gcc -g -lstdc++ -g -WI,-Bdynamic -L. -lmy -WI,-Bstatic -L. -lmy -o test.exe main.cc
 

gcc优先链接动态库,找不到,才链接静态库…… 

gcc链接时 -l 参数可否连接动态库?
g++编译时, 用-labc 选项后, 编译器会自动按照命名规则去搜索 libabc.a 库文件, 但如果我想使用动态链接库链接应如何指定参数?
优先链接共享库。共享库找不到,才链接静态库。
可是如果我在 -l 选项后添加共享库, 则链接时报告库文件不存在, 共享库和静态库的命名规则是否一样?
例如我有一个名为 libabc.so, 我应采用什么格式的选项才可以在gcc命令中找到它?
使用 -l 选项指定静态库和动态库的格式都是一样的,如果库文件名为libabc.so,那么就用 -labc 即可。
链接时会去搜索这个库文件,如果不是系统库,那么你需要告诉链接器它的路径。有两种方法:一种是在参数中用 -L 选项指定库文件搜索路径,可以并列多个。例如:gcc -L /home/ddd -L/home/ddd/lib。 另一种方法是在环境变量中设置LD_LIBRARY_PATH 包含有你的动态库文件所在的路径。这个环境变量用在Sorlarise和Linux,如果你是在HP-UX下,是SHLIB_PATH。

 

Linux设置和查看环境变量的方法

linux 查看环境变量与设置环境变量在使用过程中很常见,本文整理了一些常用的与环境变量相关的命令,感兴趣的朋友可以参考下希望对你有所帮助
 
1. 显示环境变量HOME 
$ echo $HOME 
/home/redbooks 

2. 设置一个新的环境变量hello 
$ export HELLO="Hello!" 
$ echo $HELLO 
Hello! 

3. 使用env命令显示所有的环境变量 
$ env 
HOSTNAME=redbooks.safe.org 
PVM_RSH=/usr/bin/rsh 
Shell=/bin/bash 
TERM=xterm 
HISTSIZE=1000 
... 

4. 使用set命令显示所有本地定义的Shell变量 
$ set 
BASH=/bin/bash 
BASH_VERSINFO=([0]="2"[1]="05b"[2]="0"[3]="1"[4]="release"[5]="i386-redhat-linux-gnu") 
BASH_VERSION=‘2.05b.0(1)-release‘ 
COLORS=/etc/DIR_COLORS.xterm 
COLUMNS=80 
DIRSTACK=() 
DISPLAY=:0.0 
... 

5. 使用unset命令来清除环境变量 
set可以设置某个环境变量的值。清除环境变量的值用unset命令。如果未指定值,则该变量值将被设为NULL。示例如下: 
$ export TEST="Test..." #增加一个环境变量TEST 
$ env|grep TEST #此命令有输入,证明环境变量TEST已经存在了 
TEST=Test... 
$ unset $TEST #删除环境变量TEST 
$ env|grep TEST #此命令没有输出,证明环境变量TEST已经存在了 

6. 使用readonly命令设置只读变量 
如果使用了readonly命令的话,变量就不可以被修改或清除了。示例如下: 
$ export TEST="Test..." #增加一个环境变量TEST 
$ readonly TEST #将环境变量TEST设为只读 
$ unset TEST #会发现此变量不能被删除 
-bash: unset: TEST: cannot unset: readonly variable 
$ TEST="New" #会发现此也变量不能被修改 
-bash: TEST: readonly variable 
环境变量的设置位于/etc/profile文件 
如果需要增加新的环境变量可以添加下属行 
export path=$path:/path1:/path2:/pahtN 
----------------------------------------------------------------------------------------------------------------------- 
1.Linux的变量种类 
按变量的生存周期来划分,Linux变量可分为两类: 
1.1 永久的:需要修改配置文件,变量永久生效。 
1.2 临时的:使用export命令声明即可,变量在关闭shell时失效。 

2.设置变量的三种方法 
2.1 在/etc/profile文件中添加变量【对所有用户生效(永久的)】 
用VI在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久的”。 
例如:编辑/etc/profile文件,添加CLASSPATH变量 
# vi /etc/profile 
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib 
注:修改文件后要想马上生效还要运行# source /etc/profile不然只能在下次重进此用户时生效。 
2.2 在用户目录下的.bash_profile文件中增加变量【对单一用户生效(永久的)】 
用VI在用户目录下的.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是“永久的”。 
例如:编辑guok用户目录(/home/guok)下的.bash_profile 
$ vi /home/guok/.bash.profile 
添加如下内容: 
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib 
注:修改文件后要想马上生效还要运行$ source /home/guok/.bash_profile不然只能在下次重进此用户时生效。 
2.3 直接运行export命令定义变量【只对当前shell(BASH)有效(临时的)】 
在shell的命令行下直接使用[export 变量名=变量值] 定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。 

3.环境变量的查看 
3.1 使用echo命令查看单个环境变量。例如: 
echo $PATH 
3.2 使用env查看所有环境变量。例如: 
env 
3.3 使用set查看所有本地定义的环境变量。 
unset可以删除指定的环境变量。 

4.常用的环境变量 
PATH 决定了shell将到哪些目录中寻找命令或程序 
HOME 当前用户主目录 
HISTSIZE 历史记录数 
LOGNAME 当前用户的登录名 
HOSTNAME 指主机的名称 
SHELL 当前用户Shell类型 
LANGUGE  语言相关的环境变量,多语言可以修改此环境变量 
MAIL 当前用户的邮件存放目录 
PS1 基本提示符,对于root用户是#,对于普通用户是$ 
 
 
 

以上是关于GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结的主要内容,如果未能解决你的问题,请参考以下文章

gcc/g++链接时.o文件及库的顺序问题

利用GCC编译器生成动态链接库和静态链接库

gcc编译工具生成动态库和静态库之一----介绍

GCC编译过程与动态链接库和静态链接库

Linux上静态库和动态库的编译和使用

gcc找不到静态库中的函数