简介
这是个相当基础的话题,平时也觉得知道一点。如头文件会先在当前目录查找,如果未找到会查找系统目录。
但当问题出现时,还是有点不知所措,对所谓的“系统目录”一知半解,很难把它们的清楚完整地梳理出来。
借此时机,梳理一下。
头文件
一般有两种形式的写法:双引号和尖括号,如下:
#include <iostream>
#include "demo.h"
当然也可以直接写绝对路径,那就不会有查找路径的问题了。
它们查找路径的顺序是有区别的,双引号形式会查找当前目录,而尖括号形式不会,具体查找顺序为:
- 当前目录(仅双引号形式)
- 编译时指定的头文件目录(由
gcc -I
参数指定) - 系统环境变量
CPLUS_INCLUDE_PATH
(c++头文件)或C_INCLUDE_PATH
(c头文件)指定的目录 - gcc默认目录:
/usr/include;/usr/local/include;/usr/lib/gcc/x86_64-linux-gnu/5/include
(注:最后一个路径是gcc程序的库文件地址,各个用户的系统上可能不一样)
如果各目录下存在相同的文件,则先找到哪个就使用哪个,这时顺序很重要。特别注意,尖括号形式不查找当前目录。
gcc的默认目录与安装gcc时指定的–prefix有关,该值可通过 gcc -v
查看,具体的目录可通过 echo | g++ -v -x c++ -E -
查看,如下:
#include "..." search starts here:
#include <...> search starts here:
/usr/include/c++/5
/usr/include/x86_64-linux-gnu/c++/5
/usr/include/c++/5/backward
/usr/lib/gcc/x86_64-linux-gnu/5/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
知道了头文件的查找规则,查找问题也就得心应手了。
添加路径到头文件默认搜索路径
注意,除非必要,一般不建议这样做。比较好的方式是写入到CMakeLists或者Makefile中。
- 一次性生效,命令行输入即可,只对当前shell生效,重新登录消失:
c++: export CPLUS_INCLUDE_PATH=xxx:$CPLUS_INCLUDE_PATH
c: export C_INCLUDE_PATH=XXXX:$C_INCLUDE_PATH
- 用户级生效:
修改用户的 ~/.bashrc
,在文件最后添加上述命令。执行source ~/.bashrc
或者重新登录生效。
- 全局级生效:
修改/etc/profile
文件,在文件最后添加上述命令,运行 source /etc/profile
后生效。对所有用户生效。
库文件
头文件用于编译,库文件用于链接,编译通过了,还要链接通过。链接时库文件的查找顺序如下:
- 编译时指定的库文件目录(由
gcc -L
参数指定) - 环境变量
LIBRARY_PATH
指定的目录 - 系统默认目录:
/lib; /usr/lib; /usr/local/lib
一般用户安装的库会安装在/usr/local/lib
,系统自带的库位于/lib; /usr/lib
,用户自己编译的库可能就要使用-L参数指定了。
编译时通过了,还会有一个问题,就是运行动态库的使用。因为动态库是运行时加载的,所以还会有一个查找的顺序:
- 编译时指定的动态库搜索路径(通过gcc 的参数"-Wl,-rpath,"指定。当指定多个动态库搜索路径时,路径之间用冒号
:
分隔) - 环境变量
LD_LIBRARY_PATH
指定的动态库搜索路径(路径之间用冒号:
分隔) - 配置文件
/etc/ld.so.conf
中指定的动态库搜索路径 - 默认的动态库搜索路径
/lib:/usr/lib
特别注意,库文件的查找默认是不查找当前目录的,也就是说,即使使用的库位于同一个目录内,也需要指定才能指定。
所以,当下次遇到下面这个错误时,就容易找到原因了:
./a.out: error while loading shared libraries: demo.so: cannot open shared object file: No such file or directory
添加路径到库文件默认搜索路径
注意,除非必要,一般不建议这样做。比较好的方式是写入到CMakeLists或者Makefile中。
与头文件默认路径的添加方法类似,添加的命令如下:
#动态链接库搜索路径:
export LD_LIBRARY_PATH=XXX:$LD_LIBRARY_PATH
#静态链接库搜索路径:
export LIBRARY_PATH=XXX:$LIBRARY_PATH
三个维度的生效范围同头文件。
总结
一知半解能应付照猫画虎式写代码,但是一旦写错,出现问题,就无所适从。
解决问题的根本还是要彻底弄明白事情的来龙去脉,这样才能抽丝剥茧,处理问题游刃有余。
一个再简单不过的问题依然如此,对于复杂的问题,更需要静下心来,琢磨透彻了。