源文件中明明包含了头文件,为啥编译时还是会出现未定义的引用?_引用了头文件但是还是未定义-CSDN博客

Linux下undefined reference to ‘pthread_create’问题解决_undefined reference to `pthread_detach-CSDN博客

库依赖错误导致main.c:(.text+0x7): undefined reference问题_undefined reference to `atan-CSDN博客

ubuntu下 libevent库安装和简单测试_ubuntu查看libevent是否安装-CSDN博客

如何在Ubuntu中安装libevent库_ubuntu安装libevent-CSDN博客

clion新增方法显示undefined reference to解决办法-CSDN博客

https://www.cnblogs.com/alan666/p/8311984.html

gcc -I -i -L -l 参数区别 / -l(静态库/动态库)_gcc -i-CSDN博客

linux gcc -l参数和-L参数用法含义_linux -l-CSDN博客

一、编译器相关问题

1.Linux中后缀名的含义

我们知道,Linux下文件的类型并不像windows一般依赖于其后缀名,在linux下后缀名是可有可无的但是为了区分,有些文件还是加了后缀名:

.ko 是kernel object 的缩写,是Linux 2.6内核使用的动态连接文件,在Linux系统启动时加载内核模块。
.o 是相当于windows中的.obj文件
注意:.ko与.o的区别在于,.ko是linux 2.6内核编译之后生成的,多了一些module信息,如author,license之类的。.o文件则是linux 2.4内核编译生成的。
.a 是静态库,由多个.o组成在一起,用于静态连接
.so 是shared object的缩写,用于动态连接,和windows的dll差不多
.la 为libtool自动生成的一些共享库。

先说一下C/C++编译的几个过程。

  1. 预处理,展开头文件,宏定义,条件编译处理等。通过gcc -E source.c -o source.i或者cpp source.c生成。
  2. 编译。这里是一个狭义的编译意义,指的是将预处理后的文件翻译成汇编代码的过程。通过gcc -S source.i生成。默认生成source.s文件。
  3. 汇编。汇编即将上一步生成的汇编代码翻译成对应的二进制机器码的过程。通过gcc -c source.s来生成source.o文件。
  4. 链接。链接是将生成目标文件和其引用的各种符号等生成一个完整的可执行程序的过程。链接的时候会进行虚拟内存的重定向操作。

上面四个步骤就是C/C++程序编译的几个基本步骤。前面三个步骤都是很简单,大多时候会合并为一个步骤。只有第四个步骤链接是复杂一点的。很多时候我们编译比较大的项目,报错的往往是在链接的时候缺少某些库,或者某些符号找不到定义,重定义等。

Linux中的动态库和静态库(.a/.la/.so/.o) - findumars - 博客园 (cnblogs.com)

2.GCC的参数讲解

首先指出:-l(小写L)可以连接静态库和动态库,只不过当动态库和静态库都存在的时候会优先连接动态库。

①、-g
-g可执行程序包含调试信息
-g为了调试用的
加个-g 是为了gdb 用,不然gdb用不到

②、-o
-o指定输出文件名
-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
一般语法:
gcc filename.c -o filename
上面的意思是如果你不打 -o filename(直接gcc filename.c )
那么默认就是输出a.out.这个-o就是用来控制输出文件的。 ------用./a.out执行文件

③、-c
-c 只编译不链接
产生.o文件,就是obj文件,不产生执行文件

④、-D
其意义是添加宏定义,这个很有用。

当你想要通过宏控制你的程序,不必傻乎乎的在程序里定义,然后需要哪个版本,去修改宏。

只需要在执行gcc的时候,指定-D,后面跟宏的名称即可。

示例:

gcc test.c -o test -D OPEN_PRINTF_DEBUG

或者gcc test.c -o test -DOPEN_PRINTF_DEBUG

两者都是可以的。

⑤、-w
-w的意思是关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。

⑥、-W和-Wall
-W选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告。

-Wall选项意思是编译后显示所有警告

⑦、-O3
-O是大写字母O,不是数字0哦。

意思是开启编译优化,等级为三。

⑧、-shared
如果想创建一个动态链接库,可以使用 gcc的-shared选项。输入文件可以是源文件、汇编文件或者目标文件。

⑨、-fPIC
-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)
这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
 

⑩、-I(大写i),-L,-l(小写L)的区别

例子1:

gcc -o example1 example1.c  -I /usr/local/include/freetype2 -lfreetype -lm

上面这句话在编译example1.c 时,-I /usr/local/include/freetype2 表示将/usr/local/include/freetype2作为第一个寻找头文件的目录,参数-l  (小写的i)

-lfreetype  ,-l  (小写的l)参数就是用来指定程序要链接的库-l参数紧接着就是库名。指定程序链接的库名是freetype.

-lm 表示程序指定的链接库名是m  (math数学库)

例2:

gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld

上面这句表示在编译hello.c时:

-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include 

-L /home/hello/lib表示将/home/hello/lib目录作为第一个寻找库文件的目录,寻找的顺序是:/home/hello/lib-->/lib-->/usr/lib-->/usr/local/lib

 

 -lworld表示在上面的lib的路径中寻找libworld.so动态库文件(如果gcc编译选项中加入了“-static”表示寻找libworld.a静态库文件),程序链接的库名是world

······ -L指定库的路径,-l指定需连接的库名

1。gcc包含的c/c++编译器
gcc,cc,c++,g++,gcc和cc是一样的,c++和g++是一样的,(没有看太明白前面这半句是什
么意思:))一般c程序就用gcc编译,c++程序就用g++编译

2。gcc的基本用法
gcc test.c这样将编译出一个名为a.out的程序
gcc test.c -o test这样将编译出一个名为test的程序,-o参数用来指定生成程序的名


3。为什么会出现undefined reference to 'xxxxx'错误?
首先这是链接错误,不是编译错误,也就是说如果只有这个错误,说明你的程序源码本身没有问题,是你用编译器编译时参数用得不对,你没有指定链接程序要用到得库,比如你的程序里用到了一些数学函数,那么你就要在编译
参数里指定程序要链接数学库,方法是在编译命令行里加入-lm。

4。-l  (小写的l)参数和-L  (大写的l)参数
-l参数就是用来指定程序要链接的库-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?
就拿数学库来说,他的
库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了。如果动态链接库的名字不是lib开头和.so结尾,比如说atoi.so.1文件,文件名后面的.1代表的是版本号。

动态库因为使用的时候是动态链接的,而不是直接链接到目标程序文件中的。所以可能同时存在多个版本的情况,一般都会指定版本号。但通常还是使用libxxx.so.主版本号.副版本号的形式来命名。

好了现在我们知道怎么得到库名了,比如我们自已要用到一个第三方提供的库名字叫libtest.so,那么我们只要把libtest.so拷贝到/usr/lib里,编译时加上-ltest参数,我们就能用上libtest.so库了(当然要用libtest.so库里的函数,我们还需要与libtest.so配套的头文件)。

放在/
lib/usr/lib/usr/local/lib里的库直接用-l参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find -lxxx”,也就是链接程序ld在那3个目录里找不到libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它放在/usr/X11R6/lib目录下,我们编译时就要用-L /usr/X11R6/lib -lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest。

问题:链接时使用的共享库,在可执行程序运行时找不到共享库该怎么办?

如果程序在连接时使用了共享库,就必须在运行时也要能够找到共享库的位置。

以上的-l(小写L) -L都是在链接(编译)的时候从某个指定的库路径中寻找,但是由它们生成的动态链接库.so文件如果不在系统指定的默认查找目录中,而是在其他路径中(比如当前.c所在的文件夹中),当用此动态库文件与其他.c文件链接生成的可执行文件(此时也要用-L因为这是我们自己创建的动态库文件而不是某个第三方的库文件不在默认路径中),而在运行这个可执行文件时需要装入的动态库程序时系统在默认查找路径中找不到所以会报如下错。(当然如果是静态库文件不会发生这种错误)

这个问题代表的含义是,可执行程序在加载libxxx.so库时,找不到该库。至于找不到的情况分为两种:系统里根本不存在libxxx.so库;libxxx.so库在系统中存在,但是ld找不到,即libxxx.so库的位置没有告知ld。不在默认查找路径中。

解决方法:

对于第一种情况,需要将libxxx.so放到系统ld的搜索路径中;对于第二种情况,通过locate工具定位libxxx.so的位置,然后将其放到ld的搜索路径中。

方法一:

现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。

-L: “链接”的时候,去找的目录,也就是所有的 -l 选项里的库,都会先从 -L 指定的目录去找,然后是默认的地方。编译时的-L选项并不影响环境变量LD_LIBRARY_PATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库,如果找不到,还是会报错,类似cannot open shared object file。

-rpath-link:这个也是用于“链接”的时候的,例如你显示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,后者你并没有指定,而是 FOO.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。

-rpath: “运行”的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找。对于交叉编译,交叉编译链接器需已经配置 –with-sysroot 选项才能起作用。也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库。具有可移植性,那就是编译的时候指定运行的时候共享库的加载路径。

在GCC编译程序时,由于GCC命令不经能够编译,也能够链接程序,GCC链接程序是通过ld命令实现的,如何将GCC的命令行参数传递给ld命令呢,这就是通过-Wl,来实现的。

格式如下:

gcc -Wl,param1,param2,param3,...

在调用ld命令时,会等效于如下:

ld param1 param2 param3 

注意:-Wl, 后的逗号(,)必不可少,如果要传递多个参数,参数间用,分隔

-Wl,-Bstatic

指定后续ld在处理 -l 参数来链接库文件的链接方式首选静态库

-Wl,-Bdynamic

指定后续ld在处理 -l 参数来链接库文件的链接方式首选动态库

例如: -Wl,-Bstatic -la -lb -lc -Wl,-Bdynamic -ld -l e

会被解释成 :ld liba.a libb.a libc.a libd.so libe.so

-Wl,-rpath -Wl,/data/workroom/libs/lib

会被解释成:ld -rpath /data/workroom/libs/lib

gcc使用-Wl,-rpath=sopath来指定,其中sopath是共享库放置的路径(可以是绝对路径,也可以是相对路径)。

gcc -o main main.o -Wl,-rpath,../ -lmylib
gcc -o main main.o -Wl,-rpath=. atoi.so.1 

GCC详解的-Wl选项说明-CSDN博客

【转】动态库的链接和链接选项-L,-rpath-link,-rpath | cstriker1407的笔记本

Linux中的动态库和静态库(.a/.la/.so/.o) - findumars - 博客园 (cnblogs.com)

方法二:

如果共享库文件安装到了其它"非/lib或/usr/lib" 目录下,  但是又不想在 /etc/ld.so.conf 中加路径(或者是没有权限加路径). 那可以 export 一个全局变量 LD_LIBRARY_PATH, 然后运行程序的时候就会去这个目录中找共享库. 

可以通过修改环境变量LD_LIBRARY_PATH来使系统动态载入器 (dynamic linker/loader)在指定的目录查找。 可以设置多个搜索目录, 这些目录之间用冒号分隔开.

如查找/work/lib路径,你可以在/etc/rc.d/rc.local或其他系统启动后即可执行到的脚本添加如下语句

export LD_LIBRARY_PATH =/work/lib
export LD_LIBRARY_PATH =/usr/local/lib:$(LD_LIBRARY_PATH)
//这是脚本文件才能将LD_LIBRARY_PATH作为变量使用。

我们可以使用export来设置该环境变量,在设置该环境变量后所有的命令中,该环境变量都有效。
例如:
# export LD_LIBRARY_PATH=/root/test/env/lib(对当前终端会话有效)
但本文为了举例方便,使用另一种设置环境变量的方法,
既在命令前加环境变量设置,该环境变量只对该命令有效,
当该命令执行完成后,该环境变量就无效了。如下述命令:
# LD_LIBRARY_PATH=/root/test/env/lib ./pos (仅对单个pos命令有效)
          /root/test/env/lib
程序pos运行成功,并且打印的结果是"/root/test/env/lib",
正是程序pos_env.c中的函数pos的运行结果。因此程序pos搜索到的动态库是/root/test/env/lib/libpos.so。

一般来讲这只是一种临时的解决方案, 在没有权限或临时需要的时候使用。

不过LD_LIBRARY_PATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LD_LIBRARY_PATH的缺陷和使用准则,可以参考《Why LD_LIBRARY_PATH is bad》 )。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库,避免了使用LD_LIBRARY_PATH环境变量查找。

方法三:

如果共享库文件安装到了/lib或/usr/lib目录下, 那么需执行一下ldconfig命令

ldconfig 命令的用途, 主要是在默认搜寻目录 (/lib和/usr/lib) 以及动态库配置文件 /etc/ld.so.conf 内所列的目录下, 搜索出可共享的动态链接库 (格式如lib*.so*), 进而创建出动态装入程序 (ld.so) 所需的连接和缓存文件. 缓存文件默认为 /etc/ld.so.cache, 此文件保存已排好序的动态链接库名字列表.。

如果共享库文件安装到了 /usr/local/lib (很多开源的共享库都会安装到该目录下)或其它非 /lib 或 /usr/lib 目录下, 那么在执行 ldconfig 命令前, 还要把新共享库目录加入到共享库配置文件 /etc/ld.so.conf 中,切记修改完ld.so.conf之后必须执行/sbin/ldconfig -v同步动态库;

# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
# echo "/home/xnwzh/mosquitto/mosquitto-1.5.8/lib" >> /etc/ld.so.conf
# ldconfig

更好的做法
/etc/ld.so.conf.d 目录中创建一个新的配置文件,专门用于你的项目。

示例:

bash

复制代码

sudo vim /etc/ld.so.conf.d/mosquitto.conf

内容:

bash

复制代码

/home/xnwzh/mosquitto/mosquitto-1.5.8/lib

  • 这种方式更方便管理和维护,避免直接修改系统的核心文件。

 **验证缓存:**你可以运行以下命令查看是否包含你的库:

bash

复制代码

ldconfig -p | grep libmosquitto

Linux动态库常见问题之-"cannot open shared object file No such file or directory"的解决办法-菜鸟笔记

cannot open shared object file: No such file or directory解决方法-CSDN博客

方法四:

默认搜素路径/lib、/usr/lib/指定;

上述几种方式存在一定的搜索顺序,按照搜索的先后依次为:1 > 2 > 3 > 4

方法五:
另外,大部分libxxxx.so只是一个链接,以RH9为例,比如libm.so它连接到/lib/libm.so.x,/lib/libm.so.6又链接到/lib/libm-2.3.2.so,

如果没有这样的链接,还是会出错,因为ld只会找libxxxx.so,所以如果你要用到xxxx库,而只有libxxxx.so.x或者libxxxx-x.x.x.so,做一个链接就可以了ln -s libxxxx-x.x.x.so libxxxx.so

gcc main.c test.o生成的可执行文件是静态文件还是动态文件?

gcc命令默认情况下会生成一个可执行文件,该文件通常是一个静态文件。这意味着它会包含所有必需的库和代码,以便程序能够独立地运行,而不需要依赖外部的动态链接库。

要生成一个动态文件,需要使用额外的参数指定链接器选项。例如,可以使用"-shared"选项来生成一个共享对象文件(动态链接库),或者使用"-rdynamic"选项来生成一个动态可执行文件。

总之,如果你只是使用"gcc main.c test.o"命令进行编译和链接,生成的可执行文件将是一个静态文件。

-----------------------------------------------------end----------------------------------------------------------------



手工来写链接参数总是很麻烦的,还好很多库开发包提供了生成链接参数的程序,名字一般叫xxxx-config,一般放在/usr/bin目录下,比如

gtk1.2的链接参数生成程序是gtk-config,执行gtk-config --libs就能得到以下输出"
-L /usr/lib -L /usr/X11R6/lib -lgtk -lgdk -rdynamic 

-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",这就是编译一个gtk1.2程序所需的g
tk链接参数,xxx-config除了--libs参数外还有一个参
数是--cflags用来生成头文件包含目录的,也就是-I参数,在下面我们将会讲到。你可以试试执行gtk-config --libs --cflags,看看输出结果。
现在的问题就是怎样用这些输出结果了,最笨的方法就是复制粘贴或者照抄,聪明的办
法是在编译命令行里加入这个`xxxx-config --libs --cflags`,比如编译一个gtk程序:gcc gtktest.c `gtk-config --libs --cflags`这样就差不多了。注意`不是单引号,而是1键左边那个键。

除了xxx-config以外,现在新的开发包一般都用pkg-config来生成链接参数,使用方法跟xxx-config类似,但xxx-config是针对特定的开发包,但pkg-config包含很多开发包的链接参数的生成,用pkg-config --list-all命令可以列出所支持的所有开发包,pkg-config的用法就是pkg-config pagName --libs --cflags,其中pagName是包名,是pkg-config--list-all里列出名单中的一个,比如gtk1.2的名字就是gtk+,pkg-config gtk+ --libs --cflags的作用跟gtk-config --libs --cflags是一样的。比如:
gcc gtktest.c `pkg-config gtk+ --libs --cflags`

······-include和-I(大写I)参数


-include 用来包含头文件,但一般情况下包含头文件都在源码里用#include xxxxxx实现,-include参数很少用。

-I  (大写的i)参数是用来指定头文件目录,/usr/include目录一般是不用指定的,gcc知道去那里找,但是如果头文件不在/usr/include里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I /myinclude参数了,如果不加你会得到一个"xxxx.h: No such file or directory"的错误-I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。上面我们提到的--cflags参数就是用来生成-I参数的。

·······几个环境变量的作用

  • LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
  • LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
  • PKG_CONFIG_PATH:用来指定pkg-config用到的pc文件的路径,默认是/usr/lib/pkgconfig,pc文件是文本文件,扩展名是.pc,里面定义开发包的安装路径,Libs参数和Cflags参数等等
  • 环境变量设定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx

gcc的-g,-o,-c,-D,-w,-W,-Wall,-O3等参数的意义_gcc-g-CSDN博客

gcc -I -L -l区别 - 隔壁王叔叔a - 博客园 (cnblogs.com)

-----------------------------------------------------------------------------------------------------------------------

以下的动态链接库静态链接库生成和使用直接看GITHUB也可

https://github.com/backermanaaa/GNC-Tutorial

3.Linux-(C/C++) 动态链接库生成和使用(libxxxx.so)

编译结果:生成libmax.so

运行结果:生成可执行程序main(成功了)(这里编译使用的是相对路径)(这里是修改了ld.conf用-Wl,-rpath最好)
接下来,使用绝对路径编译main(在我自己编译):

使用C++编译使用C语言提供的链接库, 编译链接出错(下面只是简单将main.c 改为main.cpp)
怎么办呢??

libmax这个库仅适合C使用,C++并不适合,如果想编译一个可以供C++使用。那么头文件(max.h)就需要改变,

需要额外增加一句:extern “C”

max.h(修改如下):

这样就解决了

但是这样有一个问题,难道每次编译都要改来改去,有没有同时适合C/C链接库的方法呢?
答案是有的,只需要改动头文件即可,使用条件编译
C++有一个宏:__cpluscplus 当用g++编译的时候,就可以识别这个宏

gcc的-g,-o,-c,-D,-w,-W,-Wall,-O3等参数的意义_gcc-g-CSDN博客

4.Linux- (C/C++)静态库生成和使用 (libxxxx.a)

Linux上的静态库,其实是目标文件的归档文件。
在Linux上创建静态库的步骤如下:

  1. 写源文件,通过 gcc -c xxx.c 生成目标文件。
  2. 用 ar 归档目标文件,生成静态库。
  3. 配合静态库,写一个使用静态库中函数的头文件。
  4. 使用静态库时,在源码中包含对应的头文件,链接时记得链接自己的库。

实例:有两个源文件第一个源文件 my_print.c,源文件2: my_math.c

使用gcc,为这两个源文件生成目标文件:

gcc -c my_print.c my_math.c

我们就得到了 my_print.o 和 my_math.o。

使用 ar 将目标文件归档:

ar crv libmylib.a my_print.o my_math.o

我们就得到了libmylib.a,这就是我们需要的静态库。

上述命令中 crv 是 ar的命令选项:

  • c 如果需要生成新的库文件,不要警告
  • r 代替库中现有的文件或者插入新的文件
  • v 输出详细信息

通过 ar t libmylib.a 可以查看 libmylib.a 中包含的目标文件。

可以通过 ar --help 查看更多帮助。

注意:我们要生成的库的文件名必须形如 libxxx.a ,这样我们在链接这个库时,就可以用 -lxxx
反过来讲,当我们告诉编译器 -lxxx时,编译器就会在指定的目录中搜索 libxxx.a 或是 libxxx.so

然后需要生成头文件

头文件定义了 libmylib.a 的接口,也就是告诉用户怎么使用 libmylib.a。

新建my_lib.h, 写入内容如下

#ifndef __MY_LIB_H__
#define __MY_LIB_H__

int add(int a, int b);
int subtract(int a, int b);

void cout(const char *);
#endif

如何使用.a静态库文件?

1.static的做法

见第五小节

2. 普遍的做法
就是直接连接.a文件全路径,这没啥好说的,就把它当.o文件一样链接。


3. 优雅的做法

既然是库,-l和-L才是正派的做法,比如同一目录下有libxxx.a文件和libxxx.so文件,gcc默认会链接so(优先使用动态库),改变这一默认行为的方法就是:将"-lxxx"改为"-l:libxxx.a"

Linux静态库生成指南 - JollyWing - 博客园 (cnblogs.com)

5.gcc参数static的用法

1 前言
gcc 编译, 有个选项是-static, 它有什么作用呢? 这主要决定, 编译-链接时, 使用的库是静态库还是动态库.

先补充一下静态库和动态库的背景.
一个模块要对外提供能力, 先有个头文件, 定义接口。 然后是库文件, 包含接口的实现。库可以是静态库和动态库。两者的区别如下:

1.1 静态库(常以a为结尾, 例如libxx.a):
在链接时将需要的二进制代码都“拷贝”到可执行文件中(注意, 只拷贝需要的,不会全部拷贝)。
优点: 编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;
缺点: 维护难, 每次更新, 都需要依赖方重新编译。 另外, 依赖方因为拷贝了库的内容, 所以编译后的文件会比较大。

1.2 动态库(常以so为结尾, 例如libxx.so):
链接时仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。
优点: 维护容易, 只要对外接口不变, 则库可以不停的更新, 依赖方不需要再次编译;
缺点: 依赖方运行时需要所有依赖的so库都存在。

所以两者的本质区别:
该库是否被编译进目标(程序)内部。

2 例子
比如,现在有个简单的程序。该程序依赖于pthread。
这个非常常见的线程模块, 在我的ubuntu的电脑的头文件和库如下:

chenxiaofeng@chenxiaofeng-HP-EliteBook-840-G3:/usr/include$ ls -la pthread.h 
-rw-r--r-- 1 root root 41269 1月  24 20:53 pthread.h

chenxiaofeng@chenxiaofeng-HP-EliteBook-840-G3:/usr/lib/x86_64-linux-gnu$ ls -la libpthread*
-rw-r--r-- 1 root root 6254274 1月  24 20:53 libpthread.a
-rw-r--r-- 1 root root     252 1月  24 20:53 libpthread.so

这个简单的程序如下:

#include <stdio.h>
#include <pthread.h>

/* this function is run by the second thread */
void *thread_exe(void *x_void_ptr)
{
    /* increment x to 100 */
    int *x_ptr = (int *)x_void_ptr;
    while(++(*x_ptr) < 100);
        printf("x increment finished\n");

    return NULL;
}

int testFunc(int param)
{
    printf(" testFunc %i\n",param);
    pthread_t inc_x_thread;
    int x = 0, y = 0;
    /* create a second thread which executes thread_exe(&y) */
    if(pthread_create(&inc_x_thread, NULL, thread_exe, &x)) {
        fprintf(stderr, "Error creating thread\n");
        return 1;
    }
    /* increment y to 100 in the first thread */
    while(++y < 100);

    printf("y increment finished\n");

    /* wait for the second thread to finish */
    if(pthread_join(inc_x_thread, NULL)) {
        fprintf(stderr, "Error joining thread\n");
       return 2;
    }

    /* show the results - x is now 100 thanks to the second thread */
    printf("x: %d, y: %d\n", x, y);
    return 0;
}

int main(int argc, char* argv[])
{
    int a = 100;
    testFunc(a);
    return 1;
}

2.1 加static编译的情况

编译:

gcc test_main.c -static -o test_main -lpthread

结果,你会发现test_main文件很大!

chenxf@chenxf-PC:~/temp/test_main$ ll
-rwxrwxr-x  1 chenxf chenxf 1131418  6月 23 14:45 test_main*
-rw-rw-r--  1 chenxf chenxf    1081  6月 23 14:38 test_main.c

这是因为,编译选择的是静态链接, 查找的库是libpthread.a, 生成目标文件, 会把各种依赖pthread的函数,比如pthread_create()函数,以及所有pthread_create()依赖的任何东西,从libpthread.a拷贝进来。 这样, 运行test_main不需要依赖任何库了!

不信??你可以用这个命令看:

nm test_main

结果一大堆,我截取一下:
 

0000000000404fb0 T __pthread_cleanup_push
0000000000404fb0 T _pthread_cleanup_push
                 w _pthread_cleanup_push_defer
                 w __pthread_cleanup_upto
0000000000402fb0 W pthread_create
0000000000402fb0 T __pthread_create_2_1
0000000000405870 T __pthread_current_priority
00000000006d6dd8 B __pthread_debug
0000000000405060 T __pthread_disable_asynccancel
0000000000405000 T __pthread_enable_asynccancel
00000000006d6df0 B __pthread_force_elision
0000000000405fe0 T __pthread_get_minstack
0000000000404cf0 T __pthread_getspecific
0000000000404cf0 T pthread_getspecific
00000000006ceec0 D __pthread_init_array
0000000000405cc0 T __pthread_initialize_minimal
0000000000405cc0 T __pthread_initialize_minimal_internal
0000000000402790 T __pthread_init_static_tls
0000000000404070 T pthread_join
0000000000404c50 T __pthread_key_create
0000000000404c50 T pthread_key_create
0000000000404cb0 T pthread_key_delete
00000000006d0d80 B __pthread_keys
00000000006d6e24 B __pthread_multiple_threads
00000000004041b0 T __pthread_mutex_lock
00000000004041b0 T pthread_mutex_lock
0000000000400390 t __pthread_mutex_lock_full
0000000000404430 T __pthread_mutex_trylock
0000000000404430 T pthread_mutex_trylock
0000000000404b60 T __pthread_mutex_unlock
0000000000404b60 T pthread_mutex_unlock
0000000000400917 t __pthread_mutex_unlock_full
0000000000404a80 T __pthread_mutex_unlock_usercnt
0000000000404f10 T __pthread_once
0000000000404f10 T pthread_once
                 w __pthread_rwlock_destroy
                 w __pthread_rwlock_init
                 w __pthread_rwlock_rdlock
                 w __pthread_rwlock_unlock
                 w __pthread_rwlock_wrlock
                 w pthread_setcancelstate
0000000000404d70 T __pthread_setspecific
0000000000404d70 T pthread_setspecific
0000000000405550 T __pthread_tpp_change_priority
00000000004061c0 T __pthread_unwind
0000000000406200 W __pthread_unwind_next
00000000004200c0 t ptmalloc_init.part.7
0000000000419fd0 t ptmalloc_lock_all
000000000041aac0 t ptmalloc_unlock_all
000000000041a0e0 t ptmalloc_unlock_all2
00000000004131c0 W puts
0000000000420b00 W pvalloc
0000000000420b00 T __pvalloc
0000000000411c30 T qsort
0000000000411900 T qsort_r
000000000044faa0 T _quicksort
000000000044fa30 T raise
000000000042d2f0 T rawmemchr
000000000042d2f0 T __rawmemchr
00000000006d7500 B _r_debug
000000000043ec60 W read
000000000043ec60 W __read
000000000040f190 t read_alias_file
0000000000469010 W readdir
0000000000469010 T __readdir
0000000000469010 W readdir64
0000000000469010 T __readdir64
00000000004099f0 t read_encoded_value_with_base
000000000040b410 t read_encoded_value_with_base
0000000000400e79 t read_int
00000000004013cb t read_int
000000000043ec69 T __read_nocancel
0000000000469aa0 T __readonly_area
0000000000406a40 t read_sleb128
0000000000409790 t read_sleb128
000000000041f830 T realloc
000000000041f830 T __realloc
000000000041ec50 t realloc_check
00000000006cf870 V __realloc_hook
00000000004204d0 t realloc_hook_ini
00000000006d6b40 b receiver
0000000000401ef0 T __reclaim_stacks
000000000049d3e0 W recvmsg
000000000049d3e0 W __recvmsg
000000000049d3e9 T __recvmsg_nocancel
0000000000441cf0 T __register_atfork
000000000040af60 T __register_frame
000000000040af50 T __register_frame_info
000000000040aed0 T __register_frame_info_bases
000000000040b010 T __register_frame_info_table
000000000040af90 T __register_frame_info_table_bases
000000000040b020 T __register_frame_table
0000000000459490 T __register_printf_function
0000000000459490 W register_printf_function
000000000045b000 T __register_printf_modifier
000000000045b000 W register_printf_modifier
00000000004593a0 T __register_printf_specifier
00000000004593a0 W register_printf_specifier
000000000045b380 T __register_printf_type
000000000045b380 W register_printf_type
0000000000401900 t register_tm_clones
00000000004002c8 r __rela_iplt_end
00000000004001d8 r __rela_iplt_start
00000000006d67e0 b release_handle
0000000000474590 t remove_slotinfo
00000000006d6500 B _res
000000000046aa30 T __res_iclose
0000000000442310 T __res_init
00000000006d74f8 B __res_initstamp
00000000004423b0 T __res_maybe_init
000000000046ab60 T __res_nclose
000000000046aa00 T __res_ninit
0000000000000000 D __resp
000000000046aa10 T __res_randomid
0000000000469c60 t res_setoptions.isra.0
000000000049fbb0 t res_thread_freeres
0000000000406220 t __restore_rt
0000000000469fd0 T __res_vinit
0000000000469120 T rewinddir
00000000004644e0 W rindex
00000000006d5490 b root
000000000048c9b0 t round_and_return
000000000048f790 t round_and_return
0000000000492190 t round_and_return
00000000006cefa0 d rtld_search_dirs
00000000006d6a80 b rule_dstoff
00000000006d6a90 b rule_stdoff
0000000000411d20 T __run_exit_handlers
00000000006d59e0 b run_fp
00000000006d6ca0 b running
00000000006d6dd0 b samples
00000000006d5b10 b save_arena
00000000004173a0 t save_for_backup
0000000000462b90 t save_for_wbackup.isra.0
00000000006d5b20 b save_free_hook
00000000006d5b30 b save_malloc_hook
000000000043f6a0 W sbrk
000000000043f6a0 T __sbrk
00000000006cf0c0 D __sched_fifo_max_prio
00000000006cf0d0 D __sched_fifo_min_prio
000000000043ea50 T __sched_getparam
000000000043ea50 W sched_getparam
000000000043ead0 T __sched_get_priority_max
000000000043ead0 W sched_get_priority_max
000000000043eaf0 T __sched_get_priority_min
000000000043eaf0 W sched_get_priority_min
000000000043ea90 T __sched_getscheduler
000000000043ea90 W sched_getscheduler
000000000043ea70 T __sched_setscheduler
000000000043ea70 W sched_setscheduler
000000000043eab0 T __sched_yield
000000000043eab0 W sched_yield
000000000040a270 t search_object
0000000000451080 W secure_getenv
00000000006d4f88 b seen_objects
000000000049d440 W sendto
000000000049d440 W __sendto
000000000049d449 T __sendto_nocancel
0000000000450d70 W setenv
0000000000450d70 T __setenv
000000000044f980 T __setfpucw
000000000049dea0 W setitimer
000000000049dea0 T __setitimer
00000000004111f0 T _setjmp
000000000044de10 T setlocale
0000000000406440 W sigaction
0000000000406440 T __sigaction
0000000000405b60 t sigcancel_handler
0000000000405bf0 t sighandler_setxid
000000000044fa00 T __sigjmp_save
0000000000411200 W siglongjmp
00000000004112b0 W sigprocmask
00000000004112b0 T __sigprocmask
000000000044f9a0 T __sigsetjmp
0000000000409980 t size_of_encoded_value
00000000004a6320 r slashdot.9308
000000000049d4a0 W socket
000000000049d4a0 T __socket
00000000004b6bba r sort_mask_chars
000000000045b500 T sscanf
000000000045b500 T __sscanf
00000000006cf0a0 d stack_cache
00000000006d0d60 b stack_cache_actsize
00000000006d0d50 b stack_cache_lock
00000000006cefe0 D __stack_prot
00000000006cf090 d stack_used
00000000006d0d30 B __stack_user
00000000006d5560 b stage
000000000040189e T _start
00000000004bf3d0 R __start___libc_atexit
00000000004bf3d8 R __start___libc_thread_subfreeres
0000000000402c40 t start_thread
00000000006d694c b state
00000000006d6954 b state
00000000006d695c b state
00000000006d6c08 b state
00000000006d6bc0 b static_buf
00000000006d4fa0 b static_slotinfo
00000000006d6df8 B __static_tls_align_m1
00000000006d6e00 B __static_tls_size
00000000006cf808 D stderr
00000000006cf818 D stdin
00000000006cf810 D stdout
00000000004b44e0 r step0_jumps.11680
00000000004b4e00 r step0_jumps.11706
00000000004b43e0 r step1_jumps.11711
00000000004b4d00 r step1_jumps.11737
00000000004b42e0 r step2_jumps.11712
00000000004b4c00 r step2_jumps.11738
00000000004b41e0 r step3a_jumps.11713
00000000004b4b00 r step3a_jumps.11739
00000000004b3fe0 r step3b_jumps.11715
00000000004b4900 r step3b_jumps.11741
00000000004b40e0 r step4_jumps.11716
00000000004b4a00 r step4_jumps.11742
00000000004b3ee0 r step4_jumps.11870
00000000004b4800 r step4_jumps.11895
00000000004bf3d8 R __stop___libc_atexit
00000000004bf3e8 R __stop___libc_thread_subfreeres
0000000000427310 i stpcpy
0000000000427310 i __stpcpy
0000000000427350 T __stpcpy_sse2
000000000043b0e0 T __stpcpy_sse2_unaligned
00000000004392a0 T __stpcpy_ssse3
0000000000427480 i strcasecmp
0000000000427480 i __strcasecmp
000000000042b2d0 T __strcasecmp_avx
0000000000427430 i __strcasecmp_l
0000000000427430 i strcasecmp_l
000000000042b2e0 T __strcasecmp_l_avx
000000000043dd00 T __strcasecmp_l_nonascii
00000000004274f0 T __strcasecmp_l_sse2
0000000000429730 T __strcasecmp_l_sse42
00000000004359b0 T __strcasecmp_l_ssse3
00000000004274e0 T __strcasecmp_sse2
0000000000429720 T __strcasecmp_sse42
00000000004359a0 T __strcasecmp_ssse3
0000000000421910 i strchr
000000000042d500 W strchrnul
000000000042d500 T __strchrnul
0000000000421940 T __strchr_sse2
000000000043b790 T __strchr_sse2_no_bsf
0000000000421b60 i strcmp
0000000000421ba0 T __strcmp_sse2
000000000042ea10 T __strcmp_sse2_unaligned
0000000000422fe0 T __strcmp_sse42
000000000042d7b0 T __strcmp_ssse3
0000000000423d90 i strcpy
0000000000423dd0 T __strcpy_sse2
000000000043aab0 T __strcpy_sse2_unaligned
0000000000437af0 T __strcpy_ssse3
0000000000423eb0 W strdup
0000000000423eb0 T __strdup
00000000004816d0 T strerror
0000000000464140 T __strerror_r
0000000000464140 W strerror_r
00000000006d7590 b string_space
00000000006d5528 b string_space_act
00000000006d5520 b string_space_max
0000000000400e07 t strip
0000000000423f00 T strlen
0000000000494da0 i strncasecmp
0000000000494da0 i __strncasecmp
0000000000499420 T __strncasecmp_avx
0000000000494d50 i __strncasecmp_l
0000000000494d50 i strncasecmp_l
0000000000499430 T __strncasecmp_l_avx
000000000049d330 T __strncasecmp_l_nonascii
0000000000494e10 T __strncasecmp_l_sse2
0000000000497460 T __strncasecmp_l_sse42
000000000049ade0 T __strncasecmp_l_ssse3
0000000000494e00 T __strncasecmp_sse2
0000000000497450 T __strncasecmp_sse42
000000000049add0 T __strncasecmp_ssse3
00000000004240c0 T strncmp
0000000000481750 i strncpy
0000000000481810 T __strncpy_sse2
0000000000484490 T __strncpy_sse2_unaligned
0000000000481910 T __strncpy_ssse3
00000000004640f0 W strndup
00000000004640f0 T __strndup
00000000004642c0 W strnlen
00000000004642c0 T __strnlen
0000000000494cb0 T strpbrk
00000000004644e0 T strrchr
0000000000481790 W strsep
0000000000481790 T __strsep
0000000000481790 T __strsep_g
00000000004263d0 i strstr
0000000000425e10 T __strstr_sse2
000000000043d220 T __strstr_sse2_unaligned
000000000048c690 W strtod
000000000048c680 T __strtod_internal
0000000000491eb0 W __strtod_l
0000000000491eb0 W strtod_l
000000000048fc60 T ____strtod_l_internal
000000000048c660 W strtof
000000000048c650 T __strtof_internal
000000000048f4b0 W __strtof_l
000000000048f4b0 W strtof_l
000000000048ce40 T ____strtof_l_internal
00000000004510b0 T strtol
000000000048c6c0 W strtold
000000000048c6b0 T __strtold_internal
0000000000494780 W __strtold_l
0000000000494780 W strtold_l
0000000000492600 T ____strtold_l_internal
00000000004510a0 T __strtol_internal
00000000004510b0 W strtoll
0000000000451580 W __strtol_l
0000000000451580 W strtol_l
00000000004510d0 T ____strtol_l_internal
00000000004510a0 T __strtoll_internal
0000000000451580 W __strtoll_l
0000000000451580 W strtoll_l
00000000004510d0 T ____strtoll_l_internal
00000000004b3c40 R __strtol_ul_max_tab
00000000004b3c00 R __strtol_ul_rem_tab
000000000048c6e0 t str_to_mpn.isra.0
000000000048f4c0 t str_to_mpn.isra.0
0000000000491ec0 t str_to_mpn.isra.0
00000000004510b0 W strtoq
00000000004120b0 T strtoul
00000000004120a0 T __strtoul_internal
00000000004120b0 W strtoull
0000000000412520 W __strtoul_l
0000000000412520 W strtoul_l
00000000004120d0 T ____strtoul_l_internal
00000000004120a0 T __strtoull_internal
0000000000412520 W __strtoull_l
0000000000412520 W strtoull_l
00000000004120d0 T ____strtoull_l_internal
00000000004120b0 W strtouq
00000000006d4dd0 b subs.9002
000000000040bb10 T __syscall_error
000000000040bb13 T __syscall_error_1
000000000043e360 W sysconf
000000000043e360 T __sysconf
000000000043e280 t __sysconf_check_spec
00000000004bd980 V _sys_errlist
00000000004bd980 V sys_errlist
00000000004bd980 R __sys_errlist_internal
00000000004bd980 R _sys_errlist_internal
00000000004bddb8 V _sys_nerr
00000000004bddb8 V sys_nerr
00000000004bddb8 R __sys_nerr_internal
00000000004bddb8 R _sys_nerr_internal
00000000004b7540 r system_dirs
00000000004b7520 r system_dirs_len
000000000041ab70 t systrim.isra.1
0000000000469810 W tcgetattr
0000000000469810 T __tcgetattr
00000000004400f0 W tdelete
00000000004400f0 T __tdelete
0000000000441410 W tdestroy
0000000000441410 T __tdestroy
000000000043f890 t tdestroy_recurse
00000000004b90e0 R __tens
00000000004bf040 R _tens_in_limb
00000000006d4dc8 b terminator.8845
00000000004019ee T testFunc
00000000006d6c30 b textsize
00000000004400a0 W tfind
00000000004400a0 T __tfind
000000000049fe68 R _thread_db_const_thread_area
000000000049fe98 R _thread_db_dtv_dtv
000000000049fe8c R _thread_db_dtv_t_pointer_val
000000000049fea4 R _thread_db_link_map_l_tls_modid
000000000049ff4c R _thread_db_list_t_next
000000000049ff40 R _thread_db_list_t_prev
000000000049fef8 R _thread_db___nptl_initial_report_events
000000000049ff04 R _thread_db___nptl_last_event
000000000049ff10 R _thread_db___nptl_nthreads
000000000049ffac R _thread_db_pthread_cancelhandling
000000000049fe80 R _thread_db_pthread_dtvp
000000000049ff7c R _thread_db_pthread_eventbuf
000000000049ff70 R _thread_db_pthread_eventbuf_eventmask
000000000049ff64 R _thread_db_pthread_eventbuf_eventmask_event_bits
000000000049febc R _thread_db_pthread_key_data_data
000000000049feb0 R _thread_db_pthread_key_data_level2_data
000000000049fec8 R _thread_db_pthread_key_data_seq
000000000049feec R _thread_db___pthread_keys
000000000049fed4 R _thread_db_pthread_key_struct_destr
000000000049fee0 R _thread_db_pthread_key_struct_seq
000000000049ffe8 R _thread_db_pthread_list
000000000049ff58 R _thread_db_pthread_nextevent
000000000049ffc4 R _thread_db_pthread_pid
000000000049ffdc R _thread_db_pthread_report_events
000000000049ff94 R _thread_db_pthread_schedparam_sched_priority
000000000049ffa0 R _thread_db_pthread_schedpolicy
000000000049ff88 R _thread_db_pthread_specific
000000000049ffb8 R _thread_db_pthread_start_routine
000000000049ffd0 R _thread_db_pthread_tid
000000000049fe70 R _thread_db_sizeof_list_t
000000000049fe7c R _thread_db_sizeof_pthread
000000000049fe70 R _thread_db_sizeof_pthread_key_data
000000000049fe6c R _thread_db_sizeof_pthread_key_data_level2
000000000049fe70 R _thread_db_sizeof_pthread_key_struct
000000000049fe74 R _thread_db_sizeof_td_eventbuf_t
000000000049fe78 R _thread_db_sizeof_td_thr_events_t
000000000049ff1c R _thread_db_td_eventbuf_t_eventdata
000000000049ff28 R _thread_db_td_eventbuf_t_eventnum
000000000049ff34 R _thread_db_td_thr_events_t_event_bits
00000000004019ae T thread_exe
0000000000465f60 T time
0000000000486520 W timelocal
00000000006d5ba8 b timestamp.10239
00000000006d6980 V timezone
00000000006d6980 B __timezone
00000000006d7540 B _tmbuf
00000000006d0cc8 D __TMC_END__
00000000004a4280 r to_mb
000000000041b1a0 t top_check
00000000006d6c70 b tos
00000000004a4300 r to_wc
0000000000469a50 W towctrans
0000000000469a50 T __towctrans
000000000040c320 t transcmp
00000000006d75a8 b transitions
00000000004b1760 r translit_from_idx
00000000004aed00 r translit_from_tbl

......

你会发现,和thread相关的各种各样的东西,全部来了!而不仅仅是pthread_create().

2.2 不加-static的情况
而如果你不加-static,结果就不同了。

gcc test_main.c -o test_main -lpthread

文件要小很多!

chenxf@chenxf-PC:~/temp/test_main$ ll
-rwxrwxr-x  1 chenxf chenxf  8891  6月 23 14:39 test_main*
-rw-rw-r--  1 chenxf chenxf  1081  6月 23 14:38 test_main.c

用nm查看,会发现东西就少很多。

chenxf@chenxf-PC:~/temp/test_main$ nm test_main
0000000000601060 B __bss_start
0000000000601068 b completed.6973
0000000000601050 D __data_start
0000000000601050 W data_start
00000000004006b0 t deregister_tm_clones
0000000000400720 t __do_global_dtors_aux
0000000000600e08 t __do_global_dtors_aux_fini_array_entry
0000000000601058 D __dso_handle
0000000000600e18 d _DYNAMIC
0000000000601060 D _edata
0000000000601070 B _end
0000000000400934 T _fini
0000000000400740 t frame_dummy
0000000000600e00 t __frame_dummy_init_array_entry
0000000000400b30 r __FRAME_END__
                 U fwrite@@GLIBC_2.2.5
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
00000000004005d8 T _init
0000000000600e08 t __init_array_end
0000000000600e00 t __init_array_start
0000000000400940 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000600e10 d __JCR_END__
0000000000600e10 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000400930 T __libc_csu_fini
00000000004008c0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000040088c T main
                 U printf@@GLIBC_2.2.5
                 U pthread_create@@GLIBC_2.2.5
                 U pthread_join@@GLIBC_2.2.5
                 U puts@@GLIBC_2.2.5
00000000004006e0 t register_tm_clones
0000000000400680 T _start
0000000000601060 B stderr@@GLIBC_2.2.5
00000000004007ad T testFunc
000000000040076d T thread_exe
0000000000601060 D __TMC_END__

其中部分关键段:

U printf@@GLIBC_2.2.5
U pthread_create@@GLIBC_2.2.5
U pthread_join@@GLIBC_2.2.5


U表示在本程序只是调用,没有定义,需要其他库支持。
T表示本程序定义。

3 总结
gcc 加上 -static,会在链接阶段, 查找对应模块的静态库, 而非动态库, 并把需要的东西,都带进目标文件),编译好后,文件会非常大,但是,运行时就不需要依赖任何动态库。

这种编译, 通常在开发阶段, 程序员使用, 很少用于量产产品喔

gcc -static 命令-CSDN博客

6.gcc搜索静态库和动态库以及头文件的顺序

静态库链接时搜索路径顺序:

1. ld(GNU linker)会去找GCC命令中的参数-L(链接时)/运行时不会链接
2.再找gcc的环境变量LIBRARY_PATH (用法:LIBRARY_PATH= path)
3.最后再找内定目录 /lib    /usr/lib    /usr/local/lib这是当初compile gcc时写在程序内的(默认值)(因系统版本而定  :/lib64)


动态库链接时、执行时搜索路径顺序:

1. 去找GCC命令中的参数-L(链接时);-Wl,rpath(运行时)
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径 (LD_LIBRARY_PATH=path)
3. 在/etc/ld.so.conf文件中添加运行时库的路径。然后执行ldconfig命令。
或者在/etc/ld.so.conf.d目录下添加一个新建的.conf新文件,然后再文件中输入新的路径,然后再执行ldconfig命令。

touch /etc/ld.so.conf.d/MyLibrary.conf

在文件里输入/home/damo/MyLibrary,然后执行ldconfig命令即可。

sudo ldconfig

4.默认的动态库搜索路径/lib(因系统版本而定:/lib64)
5.默认的动态库搜索路径/usr/lib(因系统版本而定)

Linux动态库(.so)搜索路径_linux .so库的查找路径-CSDN博客
头文件搜索路径:

1. 去 -I( i 的大写 ) 指定的路径

2.C_INCLUDE_PATH和CPLUS_INCLUDE_PATH
2. 源程序头(#include "")文件中指定的路径
3.  /usr/include
4.  /usr/local/include

Linux添加头文件和动态库的搜索路径方法总结_linux添加头文件路径-CSDN博客

7.为什么gcc编译程序时不需要写其动态链接库-lgnu

在GCC编译器中,选项"-l"用于指定需要链接的库。通常情况下,当你使用某个库时,需要在编译命令中使用"-l"选项来告诉编译器链接该库。例如,"-lgnu"用于链接名为"libgnu.so"或"libgnu.a"的GNU库。

然而,并非所有的库都需要显式地使用"-l"选项进行链接。GCC有一组默认的库搜索路径,它会自动搜索一些常见的库并进行链接。这些默认的搜索路径和库可能会因操作系统和GCC版本而有所不同。

对于一些常见的库,GCC已经默认包含了链接规则,因此你不需要显式指定"-l"选项。例如,对于GNU库,GCC会自动链接,因此你不必写"-lgnu"。

然而,这并不适用于所有的库。对于一些非常规的库或自定义的库,你可能仍然需要显式地使用"-l"选项进行链接。

总而言之,GCC在链接库时会自动搜索默认的库路径,并根据一些规则进行链接。对于常见的库,你可以省略"-l"选项,但对于非常规的库,你可能需要显式地指定链接选项。

------------------------------------------------------------------------------------------------------------------------------

Linux应用开发———编译程序提示“对“xxxxxx”未定义的引用”(依赖顺序问题)

    使用C语言做一个简单的服务器demo程序,需要用到http,于是安装了一个libghttp。

    安装完之后,在编译对应例程的时候提示:

    在安装libghttp库的时候,在配置那里使用了命令:

    于是到/usr/local/目录下查看,发现在include下有ghttp.h文件,在lib下有对应的so文件。

    之后将动态链接库拷贝到/usr/lib下,之后使用sudo ldconfig命令,还是不行。

    后来百度到:

    然后在gcc编译命令后面加上了-lghttp,就可以了:

 8.在命令行中使用某个命令

如果你在命令行中无法使用**交叉编译 GCC**,有可能是因为没有正确配置环境变量,导致系统找不到交叉编译器的路径。下面是如何排查并配置环境的步骤:

---

## **一、检查交叉编译器安装路径**

通常,交叉编译工具链的名字和路径类似:
- **arm-none-eabi-gcc**(适用于 ARM Cortex-M 系列)
 

你可以使用以下命令检查交叉编译器是否安装:
```bash
find / -name "arm-none-eabi-gcc" 2>/dev/null
```

---

## **二、设置环境变量**

如果找到了交叉编译工具链的位置,比如 `/opt/gcc-arm/bin/` 或 `/usr/local/gcc-arm/bin/`,需要将其添加到**`PATH`**环境变量中。

1. **临时配置环境变量:**
   ```bash
   export PATH=/opt/gcc-arm/bin:$PATH
   ```

2. **永久配置环境变量:**
   编辑用户的 `~/.bashrc` 文件:
   ```bash
   vim ~/.bashrc
   ```
   在文件末尾添加:
   ```bash
   export PATH=/opt/gcc-arm/bin:$PATH
   ```
   保存并退出后,执行:
   ```bash
   source ~/.bashrc
   ```

3. **验证 PATH 是否生效:**
   使用以下命令查看是否找到了交叉编译器:
   ```bash
   which arm-none-eabi-gcc
   which aarch64-linux-gnu-gcc
   ```

---

## **三、检查交叉编译器的版本**

确保交叉编译工具链能正常运行:
```bash
arm-none-eabi-gcc --version
aarch64-linux-gnu-gcc --version
```

如果能显示版本信息,说明环境变量已正确配置。

---

## **四、配置库和头文件路径(可选)**

如果交叉编译过程中找不到头文件或库文件,你还需要配置 `C_INCLUDE_PATH` 和 `LIBRARY_PATH`:

```bash
export C_INCLUDE_PATH=/opt/gcc-arm/include:$C_INCLUDE_PATH
export LIBRARY_PATH=/opt/gcc-arm/lib:$LIBRARY_PATH
```

同样,可以把这些命令添加到 `~/.bashrc` 里使其永久生效。

---

## **五、可能的错误及解决方法**

1. **“Command not found”**:可能是路径未正确配置。
   - 使用 `echo $PATH` 确保路径中包含交叉编译器所在的目录。

2. **权限不足**:
   - 如果安装在系统目录中,可能需要使用 `sudo`。
   - 确保你有权限访问交叉编译器的目录。

---

## **六、总结**

- **交叉编译器路径**要添加到 `PATH` 中。
- 使用 `~/.bashrc` 配置可确保重启后仍然有效。
- 使用 `which` 或 `--version` 检查交叉编译工具是否可用。

如果你有其他错误提示,可以提供具体的错误信息,我会进一步帮你分析。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐