C语言头文件剖析

在一个软件项目中,如果需要在一个文件中包含另一个头文件时,一般有两种包含方式:

#include <stdio.h>
#include “module.h”

如果你引用的头文件是标准库的头文件或官方路径下的头文件,一般使用尖括号<>包含;如果你使用的头文件是自定义的或项目中的头文件,一般使用双引号””包含。头文件路径一般分为绝对路径和相对路径:绝对路径以根目录/或者Windows下的每个盘符为路径起点;相对路径则是以程序文件当前的目录为起点。

#include ”/home/wit/code/xx.h”  //Linux下的绝对路径
#include “F:/litao/code/xx.h"   //Windows下的绝对路径
#include ”../lcd/lcd.h”         //相对路径,..表示当前目录的上一层目录
#include ”./lcd.h”             //相对路径,.表示当前目录
#include ”lcd.h”               //相对路径,当前文件所在的目录

编译器在编译过程中会按照这些路径信息到指定的位置去查找头文件,然后通过预处理器作展开处理。在查找头文件的过程中,编译器会按照默认的搜索顺序到不同的路径下面去搜索,以#include 为例,当我们使用尖括号<>包含一个头文件时,头文件的搜索顺序为:

  • 通过GCC参数gcc-I指定的目录(注:大写的i)
  • 通过环境变量CINCLUDEPATH指定的目录
  • GCC的内定目录
  • 搜索规则:当不同目录下存在相同的头文件时,先搜到那个使用哪个,搜索停止

当我们使用双引号“”来包含头文件路径时,编译器会首先到项目当前目录去搜索需要的头文件,在当前项目目录下面搜不到,再到其他指定的路径下面去搜索:

  • 项目当前目录
  • 通过GCC参数gcc-I指定的目录
  • 通过环境变量CINCLUDEPATH指定的目录
  • GCC的内定目录
  • 搜索规则:当不同目录下存在相同的头文件时,先搜到那个使用哪个

在编译程序时,如果我们的头文件没有放到官方路径下面,我们可以通过gcc -I来指定头文件路径,编译器在编译程序时,就会到用户指定的路径目录下面去搜索该头文件。如果你不想通过这种方式,也可以通过设置环境变量来添加头文件的搜索路径。在Linux环境下我们经常使用的环境变量有:

  • PATH:可执行程序的搜索路径
  • C_INCLUDE_PATH: C语言头文件搜索路径
  • CPLUS_INCLUDE_PATH: C++头文件搜索路径
  • LIBRARY_PATH:库搜索路径

我们可以在一个环境变量内设置多个头文件搜索路径,各个路径之间使用冒号:隔开。如果你想每次系统开机,这个环境变量设置的路径信息都生效,可以将下面的export命令添加到系统的启动脚本::~/.bashrc文件中。

export C_INCLUDE_PATH=$C_INCLUDE_PATH:/path1:/path2

除此之外,我们也可以将头文件添加到GCC内定的官方目录下面。编译器在上面指定的各种路径下面找不到对应的头文件时,最后会到GCC的内定目录下面去寻找。这些目录是GCC在安装时,通过—prefex参数指定安装路径时指定的,常见的内定目录有:

/usr/include
/usr/local/include
/usr/include/i386-linux-gnu
/usr/lib/gcc/i686-linux-gnu/5/include
/usr/lib/gcc/i686-linux-gnu/5/include-fixed
/usr/lib/gcc-cross/arm-linux-gnueabi/5/include

接下来我们做一个实验,看看实际情况跟上面说得是否一致:我们创建多个头文件module.h,分别将其放置到不同的目录下,在main()函数中分别#include这个头文件,观察程序的运行结果。

# mkdir /home/test
# cd test
# touch main.c module.h

main.c源文件的代码如下:

// main.c
#include <stdio.h>
#include "module.h"
int main (void)
{
    printf("NUM = %d\m, NUM");
    return 0;
}

//#module.h
#define NUM 10

在module.h中分别定义一个宏:NUM,然后将这个头文件拷贝到不同的目录下:

  • 将NUM定义为10,拷贝到/home/test目录下
  • 将NUM定义为20,将module.h拷贝到/usr/include目录下
  • 将NUM定义为30,将module.h拷贝到//usr

实验过程:

  • 只保留/home/test/module.h头文件,main.c中使用#include ,观察程序的编译运行结果
  • 只保留/home/test/module.h头文件,main.c中使用#include “module.h”,观察程序的编译运行结果
  • 将module.h拷贝到/home/test、/usr/include目录下,分别定义为10、20,观察程序的运行结果
  • 将module.h拷贝到/usr/include目录下,main.c中使用#include “module.h”,观察程序的运行结果

这一轮实验做下来,相信大家已经对头文件的搜索路径、搜索顺序就有了一个清晰的认识了。本文摘自《嵌入式C语言自我修养》。

本教程根据《C语言嵌入式Linux高级编程》视频教程第5期改编。《C语言嵌入式Linux高级编程》是一套专门为嵌入式设计的C语言进阶视频教程,一共9期,每期一个专题,涉及计算机体系结构、ARM汇编与反汇编、程序的编译链接原理、堆栈内存、Linux内核的面向对象编程思想、多任务编程、C语言的模块化编程等专题,具体课程详情可点击淘宝:C语言嵌入式Linux高级编程