一些GNU C的疑问
问:在C语言程序中使用#include<stdio.h>
时,为什么编译器知道文件stdio.h
的位置?
答:当在 C 语言程序中使用 #include <stdio.h>
时,编译器会根据预定义的搜索路径来查找头文件 stdio.h
,然后再搜索用户自定义的路径。
问:如何查看预定义的搜索路径?
答:可以使用编译器的预处理选项来查看系统默认的包含路径。对于 GCC 编译器,可以使用 -E
选项来进行预处理,并使用 -v
选项查看详细的编译信息,包括默认的包含路径。
在终端中执行以下命令可以查看 GCC 编译器的默认包含路径:
gcc -E -v -xc /dev/null
这个命令会输出 GCC 编译器的详细信息,其中包括搜索路径。搜索路径会以 #include <...>
形式显示在输出中,它们就是系统默认的包含路径。
输出示例:
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/usr/local/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include
/usr/local/include
/usr/local/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include-fixed
/usr/include
搜索列表结束。
可以看到,以#include "..."
引入的头文件,没有搜索路径;以#include <...>
引入的头文件,搜索路径如上所示。
其中头文件 stdio.h
就位于/usr/include
下。常见的stdlib.h
,string.h
,strings.h
,math.h
,time.h
均在这个目录下。
问:如何自定义编译器的搜索路径?
答:3.16 Options for Directory Search - GCC Docs
可以使用编译器的选项来指定其他搜索路径。例如,对于 GCC 编译器,可以使用 -I
选项来添加自定义的头文件搜索路径。比如:
gcc -I /path/to/custom/includes my_program.c -o my_program
这样就能将 /path/to/custom/includes
添加到头文件搜索路径中,让编译器能够找到你自定义的头文件。注意,如果有多个自定义的头文件路径,可以使用多个-I
参数分别指定它们。
示例:
[root@centos-llvm test] % ls
include my_program.c
[root@centos-llvm test] % ls include/
header.h
[root@centos-llvm test] % cat include/header.h
#include<stdio.h>
int add(int a, int b){
return a+b;
}
[root@centos-llvm test] % cat my_program.c
#include "header.h"
int main(){
printf("%d
",add(1,2));
}
[root@centos-llvm test] % gcc my_program.c -o my_program.o
my_program.c:1:10: 致命错误:header.h:没有那个文件或目录
1 | #include "header.h"
| ^~~~~~~~~~
编译中断。
[root@centos-llvm test] % gcc -I include my_program.c -o my_program.o
[root@centos-llvm test] % ./my_program.o
3
[root@centos-llvm test] % exit
问:如何将自定义的路径添加进编译器的默认搜索路径,让我可以通过#include <...>
引入自定义的头文件?
答:在Linux系统上,一种常用的方法是将自定义路径添加到环境变量C_INCLUDE_PATH
或CPLUS_INCLUDE_PATH
中,这取决于使用的是C语言还是C++语言。这两个环境变量指定了GCC编译器的系统包含路径。
具体步骤:
- 假设你的自定义头文件所在的文件夹路径为
/path/to/custom/includes
。 - 如果你使用的是C语言,将自定义路径添加到
C_INCLUDE_PATH
环境变量:
export C_INCLUDE_PATH=/path/to/custom/includes:$C_INCLUDE_PATH
如果你使用的是C++语言,将自定义路径添加到CPLUS_INCLUDE_PATH
环境变量:
export CPLUS_INCLUDE_PATH=/path/to/custom/includes:$CPLUS_INCLUDE_PATH
注意,通过环境变量C_INCLUDE_PATH
或CPLUS_INCLUDE_PATH
配置搜索路径,会讲其配置的搜索路径加入到编译器预定义的路径的首部:
[root@centos-llvm include]# export C_INCLUDE_PATH=/root/test/include
[root@centos-llvm include]# gcc -E -v -xc /dev/null
...
忽略不存在的目录“/usr/local/lib/gcc/x86_64-pc-linux-gnu/12.1.0/../../../../x86_64-pc-linux-gnu/include”
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/root/test/include
/usr/local/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include
/usr/local/include
/usr/local/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include-fixed
/usr/include
问:编译器搜索头文件的顺序?
答:3.16 Options for Directory Search - GCC Docs
顺序如下:
- 对于
#include "..."
引用的头文件,会首先搜索当前文件所在目录。 - 对于
#include "..."
引用的头文件,会搜索-iquote
指定的路径,如有多个,则按照命令行中从左向右的先后顺序搜索。 -I
指定的路径,如有多个,则按照命令行中从左向右的先后顺序搜索。-isystem
指定的路径,如有多个,则按照命令行中从左向右的先后顺序搜索。- 编译器预定义的路径。
-idirafter
指定的路径,如有多个,则按照命令行中从左向右的先后顺序搜索。
问:在使用gcc编译程序的时候,为什么有的flag与参数之间没有空格?如gcc -I include my_program.c
和gcc -Iinclude my_program.c
是等价的。
答:在使用gcc编译程序时,有的flag与参数之间没有空格是因为gcc编译器对于某些选项允许紧凑写法,即可以将选项与参数写在一起,也可以使用空格将它们分开。这是为了方便编译命令的书写,使命令更加简洁。
在这种紧凑写法中,不同的选项之间需要使用空格或其他分隔符来分开,以便gcc能够正确解析参数。但对于某些简单的选项,如 -I
,在参数之后紧跟着没有其他选项的情况下,可以直接写在一起,而不需要空格分隔。
需要注意的是,虽然紧凑写法使得命令更简洁,但也容易引起歧义或混淆,建议在书写编译命令时保持一致的风格,以提高代码的可读性和可维护性。