欢迎光临
我们一直在努力

源代码里通过#include引入的头文件,编译器 / 预处理器是按照什么样的规则和路径去查找并定位这些文件的

用通俗易懂的方式,从查找规则、工程配置影响、特殊情况三个维度讲清楚这个问题(以最常见的 C/C++ 为例)。

一、核心:#include 的两种语法与基础查找规则

#include有两种写法,对应完全不同的初始查找逻辑,这是编译器预处理器(编译的第一步)的核心规则:

1. 双引号形式:#include "header.h"(用于工程本地头文件)

预处理器会按以下顺序查找:

  • 第一步:先在当前源文件(.c/.cpp)所在的目录 查找这个头文件。比如main.c在/project/src目录,里面写#include "utils.h",先找/project/src/utils.h。
  • 第二步:如果第一步没找到,立刻切换到「尖括号形式」的查找规则(见下文)继续找。
  • 2. 尖括号形式:#include <header.h>(用于系统 / 标准库头文件)

    预处理器不会查当前源文件目录,直接按以下顺序查找:

  • 第一步:查找编译器 / 系统的默认标准库路径(固定路径)。
    • Linux/macOS:/usr/include、/usr/local/include等(比如<stdio.h>就在/usr/include)。
    • Windows(VS):VC\\Tools\\MSVC\\xxx\\include(VS 安装目录下的标准库路径)。
  • 第二步:查找工程 / 编译器配置的额外包含目录(你自己加的路径)。
  • 二、工程配置如何添加「自定义查找路径」

    实际工程中,头文件往往不在源文件目录或系统默认路径里(比如放在include/、third_party/目录),这时候需要通过配置告诉编译器去哪里找,核心方式有 3 种:

    1. 编译命令行(最底层)

    用gcc/clang/msvc编译时,通过-I参数(大写 i)指定「包含目录」,多个目录用多个-I:

    bash

    运行

    # 示例:编译main.c,指定两个自定义包含目录
    gcc main.c -I./include -I../third_party -o app

    这里-I./include表示:把当前目录下的include文件夹加入查找路径,预处理器会去这里找头文件。

    2. IDE 工程配置(可视化操作)

    比如 VS、CLion、Qt Creator 等 IDE,都有专门的「包含目录」设置项:

    • VS:右键工程 → 属性 → C/C++ → 常规 → 附加包含目录(添加你的路径)。
    • CLion/CMake 工程:见下文。
    3. 构建工具配置(Makefile/CMake)

    大型工程不会手动输编译命令,而是用构建工具管理:

    • Makefile:通过CFLAGS/CXXFLAGS变量添加-I路径:

      makefile

      # Makefile示例
      CFLAGS = -I./include -I../third_party # C语言编译参数
      CXXFLAGS = -I./include -I../third_party # C++编译参数
      app: main.c utils.c
      gcc $(CFLAGS) main.c utils.c -o app

    • CMake(最常用):用include_directories(全局)或target_include_directories(推荐,针对目标):

      cmake

      # CMakeLists.txt示例
      cmake_minimum_required(VERSION 3.10)
      project(my_app)

      # 方式1:全局包含目录(所有目标都能用)
      include_directories(${CMAKE_SOURCE_DIR}/include)
      # 方式2:仅针对my_app目标(推荐)
      add_executable(my_app main.c utils.c)
      target_include_directories(my_app PRIVATE ${CMAKE_SOURCE_DIR}/third_party)

    三、补充:特殊情况与注意点

  • 路径优先级:当前源文件目录("" 专属) > 自定义包含目录(-I) > 系统默认路径。比如两个目录都有utils.h,编译器会优先用「当前源文件目录」的。

  • 嵌套 #include:如果a.h里包含了#include "b.h",查找b.h的规则不变 —— 依然基于原始源文件(比如main.c)的目录,除非a.h里用了相对路径(比如a.h在dir1,写#include "sub/b.h",则找dir1/sub/b.h)。

  • 绝对路径 vs 相对路径:

    • 绝对路径(比如#include "/project/include/utils.h"):直接定位该路径,跳过所有查找规则。
    • 相对路径(比如#include "../include/utils.h"):基于当前源文件目录向上找一级,再找include/utils.h。
  • 头文件重复包含:虽然和「查找」无关,但新手常遇到 —— 多次#include同一个头文件会导致编译错误,解决方式是加「头文件保护」:

    c

    运行

    // utils.h 开头和结尾加保护
    #ifndef UTILS_H // 如果没定义这个宏
    #define UTILS_H // 定义宏

    // 头文件内容
    int add(int a, int b);

    #endif // 结束保护
    // 或简化写法(编译器支持):#pragma once

  • 总结

  • #include的两种形式(""和<>)是查找的起点:""先查当前源文件目录,<>直接查系统 / 配置路径。
  • 工程通过-I编译参数、IDE「附加包含目录」、CMake/Makefile 配置,添加自定义头文件查找路径。
  • 查找优先级:当前源文件目录 > 自定义包含目录 > 系统默认标准库目录。
  • 掌握这三点,就能清晰控制工程中头文件的查找逻辑,避免「找不到头文件」(fatal error: xxx.h: No such file or directory)这类常见错误。

    赞(0)
    未经允许不得转载:171主机测评 » 源代码里通过#include引入的头文件,编译器 / 预处理器是按照什么样的规则和路径去查找并定位这些文件的
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址