GCC编译选项系列—— -Idir(指定头文件)
这个选项使用的频率比较高!用于指定头文件所在目录!
0、本文所使用环境
硬件 :龙芯3A5000
OS :Loongnix Server 8.4
1、引言
在C/C++代码中,使用#include语句包含头文件,使用#include " "
样式时,编译器首先在编译目录下查找所引用的头文件,如果没有找到,编译器会继续在默认路径中进行查找;如果使用的是#include < >
样式,则只会在默认路径下查找。
有时我们安装库的头文件没有在标准目录下(Linux中很常见),那么此时就可以使用-Idir
方式来指定头文件所在目录。指定之后,编译器首先会在-I
所指定的目录下去查找头文件,找不到后才会去默认路径下进行查找。
2、使用-I指定头文件所在目录
下面所用示例代码test.c引用自:https://zhuanlan.zhihu.com/p/34903301(作者原文已注明可转载)
#include <glib.h>
#include <glib/gprintf.h>
// DATA STRUCTURE
typedef struct Value_T {
gint iKeyCopy;
gint iItem1;
gint iItem2;
} Value_T;
// RELASE CALLBACK
void func_destroy_notify_for_value(gpointer data) {
Value_T* pp = (Value_T*)data;
if (pp == NULL)
return;
g_print("func-destroy-notify-for-test: KEY:%d ITEM1:%d ITEM2:%d \n", pp->iKeyCopy, pp->iItem1, pp->iItem2);
g_free(pp);
pp = NULL;
}
// MAIN
int main (int argc, const char* argv[]) {
gint iKey = 20160222;
// CREATE
GHashTable* ptHashTable = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, func_destroy_notify_for_value);
if (ptHashTable == NULL) {
g_print("main: create hashtable failed\n");
return -1;
}
// INSERT
if (ptHashTable) {
Value_T* ptVal = g_new0(Value_T, 1);
if (ptVal) {
ptVal->iKeyCopy = iKey;
ptVal->iItem1 = 123;
ptVal->iItem2 = 456;
g_hash_table_insert(ptHashTable, GINT_TO_POINTER(iKey), ptVal);
} else {
g_print("main: cannot get new memory to insert, error\n");
}
}
// LOOKUP OK
if (ptHashTable) {
Value_T* pp = (Value_T*)g_hash_table_lookup(ptHashTable, GINT_TO_POINTER(iKey));
if (pp) {
g_print("main: lookup ok for key:%d : %d %d\n", iKey, pp->iItem1, pp->iItem2);
} else {
g_print("main: lookup fail for key:%d\n", iKey);
}
}
// LOOKUP FAIL
if (ptHashTable) {
Value_T* pp = (Value_T*)g_hash_table_lookup(ptHashTable, GINT_TO_POINTER(iKey+1));
if (pp) {
g_print("main: lookup ok for key:%d : %d %d\n", iKey+1, pp->iItem1, pp->iItem2);
} else {
g_print("main: lookup fail for key:%d\n", iKey+1);
}
}
// RELEASE
if (ptHashTable) {
g_hash_table_remove_all(ptHashTable);
g_hash_table_destroy(ptHashTable);
ptHashTable = NULL;
}
return 0;
}
在命令行中直接使用-Idir方式指定头文件所在目录,此处先忽略-l(小写L)指定库名称,这个在后续文章中再做解释。
gcc -std=c99 -g -Wall -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -lgthread-2.0 -pthread -lglib-2.0 -o test.elf test.c
如果头文件安装到非默认路径的库很多,这样一个一个去指定,这明显性就有些不太合理了。我们遇到的问题,那都是别人已经见过的了,所以在Linux中,就有这么一个工具专门来做这件事,那就是pkg-config
。
3、pkg-config
3.1、pkg-config常用选项
[loongson@bogon ~]$ pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
[loongson@bogon ~]$ pkg-config --libs glib-2.0
-lglib-2.0
3.2、用pkg-config方式来编译前面的示例
这条编译命令同样转载自:https://zhuanlan.zhihu.com/p/34903301(作者原文已注明可转载)
gcc -std=c99 -g -Wall `pkg-config --cflags --libs glib-2.0 gthread-2.0` -o test.elf test.c
3.3、更进一步
这么做还是有些复杂了,更进一步,Linux中软件包在编译的时候,会有一个pkgsname.pc的文件,来记录这一切。而这些pkgsname.pc位于/usr/lib64/pkgconfig/目录下。
[loongson@bogon ~]$ more /usr/lib64/pkgconfig/glib-2.0.pc
prefix=/usr
exec_prefix=/usr
libdir=/usr/lib64
includedir=/usr/include
glib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenums
Name: GLib
Description: C Utility Library
Version: 2.56.4
Requires.private: libpcre
Libs: -L${libdir} -lglib-2.0
Libs.private: -pthread -lpcre
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include
4、查看头文件默认搜索路径
另外在多说一句,Loongnix(CentOS8)中,查看头文件默认搜索路径的命令如下:
[loongson@bogon ~]$ echo 'main(){}' | /usr/bin/gcc -E -v -
......
ignoring nonexistent directory "/usr/lib/gcc/loongarch64-loongson-linux-gnu/8/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/loongarch64-loongson-linux-gnu/8/../../../../loongarch64-loongson-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/loongarch64-loongson-linux-gnu/8/include
/usr/local/include
/usr/include
End of search list.
......
5、额外的实验
在Linux下写C/C++代码的人,基本上都知道gcc将编译分为4个阶段:预处理(-E
)、编译(-S
)、汇编(-c
)、链接。那么本文所说的查找头文件是在那一步完成的呢?
先说结论:查找头文件在预处理阶段完成。
还是以前面的示例为基础:
1)第一条命令,因为只进行了预处理,所以暂时可以不需要动态库信息。
2)第二条命令,当少了一个库所在头文件时,预处理阶段出错了
# 此条命令与上面2中完全一致,放在这里只是为了方便对比
[loongson@bogon gcc]$ gcc -std=c99 -g -Wall -E -I/usr/include/glib-2.0 -lgthread-2.0 -pthread -lglib-2.0 -o test.elf test.c
# 第一条命令
[loongson@bogon gcc]$ gcc -std=c99 -g -Wall -E -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include test.c > test.i
# 第二条命令
[loongson@bogon gcc]$ gcc -std=c99 -g -Wall -E -I/usr/include/glib-2.0 test.c > test.i
In file included from /usr/include/glib-2.0/glib/galloca.h:32,
from /usr/include/glib-2.0/glib.h:30,
from test.c:1:
/usr/include/glib-2.0/glib/gtypes.h:32:10: fatal error: glibconfig.h: No such file or directory
#include <glibconfig.h>
^~~~~~~~~~~~~~
compilation terminated.
[loongson@bogon gcc]$
6、头文件相关环境变量
本章内容来自gcc官方文档。
除了上面的方法,在Linux中,还有四个环境变量可以用来设置预处理阶段头文件搜索路径:C_INCLUDE_PATH
(用于C语言)、CPP_INCLUDE_PATH
(用于C++)、CPATH
(都可以用)、OBJC_INCLUDE_PATH
(这个我没用到,应该是苹果公司的那个)。
在Loongnix Server 8.4中,这三个环境变量的值均为空:
[loongson@bogon gcc]$ echo $C_INCLUDE_PATH
[loongson@bogon gcc]$ echo $CPP_INCLUDE_PATH
[loongson@bogon gcc]$ echo $CPATH
[loongson@bogon gcc]$
在Linux中,它们的取值可以是一组用:
分割开的地址列表,类似于环境变量PATH
。
CPATH
指定了一个要搜索的目录列表,就像用-I
指定的那样,但是优先级低于使用-I
指定的目录。
其余几个环境变量只适用于预处理特定的语言,每个变量都指定了一个要搜索的目录列表,就像用-isystem
指定的那样,但是优先级低于-isystem
所指定的目录
6.1、-isystem解释
-isystem dir
Search dir for header files, after all directories specified by -I but before the standard system directories.
Mark it as a system directory, so that it gets the same special treatment as is applied to the standard system directories.
6.2、注意事项
在这些环境变量中,空元素指示编译器搜索其当前工作目录。空元素可能出现在路径的开头或结尾。例如,如果CPATH的值是:/special/include
,这就相当于'-I. -I/special/include'
。
7、其他相关的gcc选项
7.1、-I-
这个选项的功能就是取消-Idir的设置,常用于-Idir之后。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!