首页 > 代码库 > gcc在编译时头文件路径和动态链接库路径

gcc在编译时头文件路径和动态链接库路径

在使用gcc编译连接生成可执行文件时,经常会碰到变量未定义、链接时或者运行可执行文件时找不到相应的动态库等问题,本文首先介绍了gcc在编译时头文件路径相关选项以及搜索路径顺序,然后讨论了编译成可执行文件时动态库的搜索路径顺序,最后说明了生成可执行文件后,运行文件时动态库的搜索路径顺序。搞清楚这三个搜索路径,在用gcc碰到的未定义变量或搜索不到动态库的问题,基本上都可以解决了。

头文件路径编译时相关选项
    gcc可以使用选项-I(注意是大写)来指定头文件搜索路径,即头文件所在的文件夹。指定的路径即可以是绝对路径也可以是相对路径。比如当前路径下有文件hello.c和include/testhead.h,则有两种方法访问这个头文件:
   hello.c中#include "include/testhead.h"直接指定头文件的路径,然后可以直接编译不需要选项-I了,即gcc hello.c。
   Hello.c中#include "testhead.h",在编译时使用:gcc -I include hello.c或者gcc -I ./include hello.c,在编译选项中指定头文件的路径。
另外选项-nostdinc使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置。

两种不类型的头文件#include<>和#include""搜索规则
  1)使用<>包含的头文件一般会先搜索-I选项后的路径(即用gcc编译时的-I选项,注意是大写),之后就是标准的系统头文件路径。
  2)而用""号包含的头文件会首先搜索当前的工作目录,之后的搜索路径才是和<>号包含的头文件所搜索的路径一样的路径。

头文件搜索顺序
  头文件的搜索顺序,可以在编译时使用选项-v查看编译详情里会列出,比如使用gcc -v -I ./include hello.c相关部分如下:

      
  头文件搜索顺序是不会递归在目录下搜索的,搜索顺序规则如下(这里是指<>类型头文件搜索规则,因为””类型头文件首先搜索当前的工作目录,之后的搜索路径是和<>号包含的头文件所搜索的路径一样):
  首先由参数-I指定的路径(指定路径有多个路径时,按指定路径的顺序搜索)。
  然后找gcc的环境变量 C_INCLUDE_PATH(这个变量是搜索C语言的头文件使用)和CPLUS_INCLUDE_PATH(针对C++的头文件),设置环境变量的方法与通常是一样的,比如在.bashrc或.profile中添加,比如:

  export C_INCLUDE_PATH="$HOME/github"
  最后找默认的目录
  /usr/local/include
  libdir/gcc/target/version/include
  /usr/target/include
  /usr/include
对于C++程序,还会在libdir/../include/c++/version中查找这里的target是指GCC的编译配置的标准名字,version是GCC在用的版本。比如用gcc -v hello.cpp得到的相关路径查找信息是:
  

  动态库相关选项
  假如我们有两个源文件,一个是dltest.c代码如下:

  #include <stdio.h>
  void test_dl()
  {
      printf("Test DL\n");                                                                               
  }
另外一个源文件代码hello.c如下:
  void test_dl();
  int main(void)
  {
  test_dl();
  return 0;
  }
通过执行命令gcc -shared -fpic -o libdltest.so dltest.c来创建动态库文件libdltest.so。这里选项-shared告诉连接器源码是生成一个共享库,而不是可执行文件。选项-fpic参数声明链接库的代码段是可以共享的,使生成的对象模块采用浮动的(可重定位的)地址,pic 代表“位置无关代码”(position independent code)。请注意这次我们编译的共享库的名字叫做libdltest.so,这也是Linux共享库的一个命名的惯例了:后缀使用so,而名称使用libxxxx格式。创建好共享库后,就可以使用命令gcc -L./ -ldltest hello.c来连接动态库生成可执行文件。选项-L是把其后路径加入到搜索库文件的路径列表中;选项-l指定搜索指定名的库。注意使用的名字是dltest,而链接的是libdltest.so,这种方式是linux通常做法。编译生成可执行文件时,动态库的搜索路径顺序如下:
  首先gcc会找-L选项;
  然后再找gcc的环境变量LIBRARY_PATH,可以在.profile设置这个环境变量,并且可以通过选项-v查看gcc最终编译时LIBRARY_PATH的值;
  再找内定目录 /lib:/usr/lib: /usr/local/lib,这些都是当初compile gcc时写在程序内的。
注意上面索顺序是不会递归在目录下搜索的。生成可执行文件后,运行文件时,动态库的搜索路径顺序如下:
  首先编译目标代码时指定的动态库搜索路径,就是用选项-Wl,rpath指定程序在运行时动态库的搜索路径,比如gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径./include;
  环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  配置文件/etc/ld.so.conf中指定的动态库搜索路径,即在配置文件中添加动态库的绝对路径,然后运行指令ldconfig是配置文件生效;
  默认的动态库搜索路径/lib;
  默认的动态库搜索路径/usr/lib。
同样上面索顺序是不会递归在目录下搜索的。通常使用动态库简单做法是:把生成的so文件拷贝到/usr/lib中,这样不管是生成可以执行文件时,还是执行程序时,都能找到需要的so文件。

参考资料

https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html
http://www.network-theory.co.uk/docs/gccintro/gccintro_23.html
https://gcc.gnu.org/onlinedocs/gcc-4.9.1/cpp/Search-Path.html#Search-Path  
https://erex.sinaapp.com/?p=126