Linux C/C++头文件和库文件搜索路径

在linux中使用gcc编译C/C++程序,当我们没有安装相应依赖库或者依赖库不在gcc搜索路径时,总是会提示找不到头文件,又或者找不到库文件,那么gcc是根据什么去找到对呀的文件的呢?

编译链接

头文件

预处理时头文件的搜索路径顺序为:

  1. 搜索当前目录(仅当使用#include””时搜索当前目录,#include<>不搜索当前目录)
  2. 搜索-I指定的目录
  3. 环境变量指定的目录
    • C语言使用的是: C_INCLUDE_PATH
    • C++程序使用的是: CPLUS_INCLUDE_PATH
    • Objective-C程序使用的是: OBJC_INCLUDE_PATH
  4. 搜索gcc的内定目录:
    • /usr/lib64/gcc/x86_64-suse-linux/9/include
    • /usr/local/include
    • /usr/lib64/gcc/x86_64-suse-linux/9/include-fixed
    • /usr/x86_64-suse-linux/include
    • /usr/include

各目录存在相同文件时,先找到哪个使用哪个。

库文件

链接时库的搜索路径为:

  1. 搜索-L指定的目录
  2. 搜索环境变量LIBRARY_PATH指定目录
  3. 搜索gcc内定目录:
    • /usr/lib64/gcc/x86_64-suse-linux/9
    • /usr/lib64
    • /lib64
    • /usr/lib64
    • /usr/x86_64-suse-linux/lib
    • /usr/lib64
    • /lib
    • /usr/lib

备注

这里说下gcc内置搜索目录,它的部分目录和编译gcc时指定的--prefix 有关,我们可以使用命令查看相关信息:

# 这里使用自己编译的gcc-10.2.0版本
# 编译命令为./configure --prefix=/opt/gcc --disable-multilib
/opt/gcc/bin/gcc -v t.c
Using built-in specs.
COLLECT_GCC=/opt/gcc/bin/gcc
COLLECT_LTO_WRAPPER=/opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/opt/gcc --disable-multilib
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.2.0 (GCC) 
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/cc1 -quiet -v t.c -quiet -dumpbase t.c -mtune=generic -march=x86-64 -auxbase t -version -o /tmp/ccBMdiqn.s
GNU C17 (GCC) version 10.2.0 (x86_64-pc-linux-gnu)
        compiled by GNU C version 10.2.0, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version isl-0.18-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/include
 /usr/local/include
 /opt/gcc/include
 /opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/include-fixed
 /usr/include
End of search list.
GNU C17 (GCC) version 10.2.0 (x86_64-pc-linux-gnu)
        compiled by GNU C version 10.2.0, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version isl-0.18-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 59a2f270b2c4e34209979b82c683a113
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/cc8nHkKq.o /tmp/ccBMdiqn.s
GNU assembler version 2.32.0 (x86_64-suse-linux) using BFD version (GNU Binutils; openSUSE Leap 15.2) 2.32.0.20190909-lp152.3
COMPILER_PATH=/opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/:/opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/:/opt/gcc/lib/gcc/x86_64-pc-linux-gnu/:/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/:/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/
LIBRARY_PATH=/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/:/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/collect2 -plugin /opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/liblto_plugin.so -plugin-opt=/opt/gcc/lib/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/cchRby5t.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/../lib64/crt1.o /usr/lib/../lib64/crti.o /opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/crtbegin.o -L/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0 -L/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. /tmp/cc8nHkKq.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /opt/gcc/lib64/gcc/x86_64-pc-linux-gnu/10.2.0/crtend.o /usr/lib/../lib64/crtn.o
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'

如果不希望使用内置的目录可使用-nostdinc (C++使用-nostdinc++)参数进行屏蔽。

运行时

运行时动态库的搜索路径先后顺序为:

  1. 编译目标代码时指定的动态库搜索路径(通过gcc的参数-Wl,-rpath,指定)
  2. 环境变量LD_LIBRARY_PATH 指定的动态库搜索路径
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认的动态库搜索路径:
    • /lib
    • /usr/lib

注意,与windows不同,linux中动态库的搜索路径默认情况下并不包含当前目录或当前目录下的lib目录,所以linux中,将so与可执行程序放置在同一个目录下面并不能解决找不到链接库的问题,如果需要达到类似windows的效果,可以配置LD_LIBRARY_PATH 环境变量或通过编译参数-Wl,-rpath,.:lib以达到效果。

关于LD_xxx/etc/ld.so.xxx 还有很多其他的用途与优先级问题,这里不详述。

常用的环境变量

在编译的时候我们需要用到其他的库,在config时候可以通过“-I”来指定头文件目录,但是每次都需要设置的话难免有些麻烦,找到一个简单的方法。 \n 有大量的环境变量可供设置以影响 GCC 编译程序的方式。有一些环境变量要设置为列表,条目之间使用特殊字符PATH_SEPARATOR分隔。在UNIX系统中分隔符为:,而在Windows系统中分隔符为;

  • C_INCLUDE_PATH

    编译 C 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项一样。会首先查找 -isystem 指定的所有目录。 \n 也见 CPATH 、 CPLUS_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。

  • COMPILER_PATH

    该环境变量指定一个或多个目录名列表,如果没有指定 GCC_EXEC_PREFIX 定位子程序,编译程序会在此查找它的子程序。 \n 也见 LIBRARY_PATH 、 GCC_EXEC_PREFIX 和 -B 命令行选项。

  • CPATH

    编译 C 、 C++ 和 Objective-C 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -l 选项一样。会首先查找 -l 指定的所有目录。 \n 也见 C_INCLUDE_PATH 、 CPLUS_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。

  • CPLUS_INCLUDE_PATH

    编译 C++ 程序时使用该环境变量。该环境变量指定一个或多个目录名列表,查找头文件,就好像在命令行中指定 -isystem 选项一样。会首先查找 -isystem 指定的所有目录。 \n 也见 CPATH 、 C_INCLUDE_PATH 和 OBJC_INCLUDE_PATH 。

  • DEPENDENCIES_OUTPUT

    为文件名设置该环境变量会让预处理程序将基于依赖关系的 makefile 规则写入文件。不会包括系统头文件名字。 \n 如果环境变量设置为单名,被看作是文件名字,而依赖关系规则的名字来自源文件名字。如果定义中有两个名字,则第二个名字是用作依赖关系规则的目标名。 \n 设置该环境变量的结果和使用命令行选项 -MM 、 -MF 和 -MT 的组合是一样的。也见 SUNPRO_DEPENDENCIES 。

  • GCC_EXEC_PREFIX

    如果定义了该环境变量,它会作为编译程序执行的所有子程序名字的前缀。例如,如果将变量设置为 testver 而不是查找 as ,汇编器首先会在名字testveras 下查找。如果在此没有找到,编译程序会继续根据它的普通名进行查找。可在前缀名中使用斜线指出路径名。 \n GCC_EXEC_PREFIX 的默认设置为prefix /lib/gcc-lib/ ,这里的 prefix是安装编译程序时 configure 脚本指定的名字。该前缀也用于定位标准连接程序文件,包含进来作为可执行程序的一部分。 \n 如果使用 -B 命令行选项,会重写该设置。也见 COMPILER_PATH 。

  • LANG

    该环境变量用于指出编译程序使用的字符集,可创建宽字符文字、串文字和注释。 \n 定义 LANG 为 C-JIS ,指出预处理程序将多字节字符按照 JIS (日语工业标准)字符进行解释。 C-SJIS 可用来指出 Shift -JIS 字符而 C-EUCJP 指出日文 EUC 。 \n 如果没有定义 LANG ,或定义为不可识别,函数 mblen() 被用来确定字符宽度,而 mbtowc() 用来将多字节序列转换为宽字符。 \n LC_ALL 如果设置,该环境变量的值重写 LC_MESSAGES 和 LC_CTYPE 的所有设置。 \n LC_CTYPE 该环境变量指出引用串中定义的多字节字符的字符分类。主要用于确定字符串的字符边界,字符编码需要用引号或转义符,可被错误地解释为字符串的结尾或特殊字 符串。对 Australian English ,可将它设置为 en_AU ; 对 Mexican Spanish ,可将它设置为 es_MX。如果没有设置该变量,默认为 LANG 变量的值,或如果没有设置 LANG ,那就使用 C 英语行为。也见 LC_ALL 。 \n LC_MESSAGES 该环境变量指出编译程序使用何种语言发出诊断消息。对 Australian English ,可设置为 en_AU ;对 MexicanSpanish ,可设置为 es_MX 。如果变量没有设置,使用 LANG 变量的默认值,或如果没有设置 LANG ,那就使用 C英语行为。也见 LC_ALL 。

  • LD_LIBRARY_PATH

    该环境变量不会影响编译程序,但程序运行的时候会有影响。变量指定一个目录列表,程序会查找该列表定位共享库。只有当未在编译程序的目录中找到共享库的时候,执行程序必须设置该变量。

  • LD_RUN_PATH

    该环境变量不会影响编译程序,但程序运行的时候会有影响。该变量在运行时指出文件的名字,运行的程序可由此得到它的符号名字和地址。地址不会重新载入,因而可能符号引用其他文件中的绝对地址。这和 ld 工具使用 -R 选项完全一样。

  • LIBRARY_PATH

    该环境变量可设置为一个或多个目录名字列表,连接程序会搜寻该目录,以查找特殊连接程序文件,和由 -l (字母 l)命令行选项指定名字的库。 \n 由 -L 命令行选项指定的目录在环境变量的前面,首先被查找。也见 COMPILER_PATH 。

  • OBJC_INCLUDE_PATH

    在编译 Objective-C 程序的时候使用该环境变量。一个或多个目录名的列表由环境变量指定,用来查找头文件,就好像在命令行中指定 -isystem 选项一样。所有由 -isystem 选项指定的目录会首先被查找。 \n 也见 CPATH 、 CPLUS_INCLUDE_PATH 和 C_INCLUDE_PATH 。

  • SUNPRO_OUTPUT

    为文件名设置该环境变量会令预处理程序将基于依赖关系的 makefile 规则写入文件。会包含系统头文件名。 \n 如果环境变量被设置为单个名字,它将会被当作文件名,依赖关系规则中的名字将由源文件的名字中获得。如果定义中有两个名字,第二个名字就是依赖关系规则中的目标名。 \n 设置该环境变量的结果与在命令行中使用参数 -M 、 -MF 和 -MT 的效果一样。参见 DEPENDENCIES_OUTPUT 。

  • TMPDIR

    这个变量包含了供编译程序存放临时工作文件的目录的路径名。这些文件通常在编译过程结束时被删除。这种文件的一个例子就是由预处理程序输出并输入给编译程序的文件。

cmake相关环境变量

  • CMAKE_MODULE_PATH

    在cmake中使用find_package命令查找外部库时,CMake 会到变量CMAKE_MODULE_PATH指示的目录下查找文件Findname.cmake并执行。只要找到包,就会定义下面这些变量(都在Findname.cmake文件中设置):

    • _FOUND
    • _INCLUDE_DIRS or _INCLUDES
    • _LIBRARIES or _LIBRARIES or _LIBS
    • _DEFINITIONS

    要使用库name,我们在顶层目录中的CMakeLists.txt文件中,检查变量NAME_FOUND来确定包是否被找到(大部分包的这些变量中的包名是全大写的,有些包则使用包的实际大小写)

    如果找到这个包,我们用NAME_INCLUDE_DIRS调用include_directories()命令,用NAME_LIBRARIES调用target_link_libraries()命令。

  • PKG_CONFIG_PATH

    pkg_check_modules是CMake自己的pkg-config模块的一个用来简化的封装: 你不用再检查CMake的版本,加载合适的模块,检查是否被加载,等等,参数和传给find_package的一样: 先是待返回变量的前缀,然后是包名(pkg-config的)。这样就定义了<prefix>_INCLUDE_DIRS和其他的这类变量,后续的用法就与find_package一致。pkg_check_modules实质上是检测系统中的pkg-config是否存在指定的.pc文件。

    pkg-config是在编译应用程序和库时使用的辅助工具,它可以帮助您在命令行上插入正确的编译器选项。如:gcc -o test test.c `pkg-config --libs --cflags glib-2.0`。当安装一个使用pkg-config的库时,库会包括一个后缀名为pc的文件,它会放入某个文件夹下 (依赖于你的系统设置,例如: Linux 为该库文件所在文件夹/lib/pkgconfig)。pkg-config会将环境变量 PKG_CONFIG_PATH 加入搜索路径。