C++ include头文件的顺序以及双引号““和尖括号<>的区别

devtools/2024/10/17 13:24:18/

本文章进一步详细解释 #include 的头文件包含机制,包括搜索路径的处理、双引号 "" 和尖括号 <> 在不同环境中的使用差异,以及它们的底层机制。

1. 头文件包含机制和搜索路径详解

#include 是一个预处理指令,用于在编译前将指定的头文件的内容插入到当前源文件中。在 C++ 编译过程中,头文件的查找顺序和搜索路径的选择会影响项目的编译成功与否。

双引号 "" 的搜索路径机制

当使用双引号 #include "filename" 包含头文件时,编译器通常按照以下顺序查找头文件:

  1. 当前文件所在的目录
    • 编译器首先查找与当前源文件同目录的头文件。这是因为我们通常使用双引号来包含本地定义的头文件(如类定义的 .h 文件)。
  2. 包含该文件的文件夹
    • 如果该源文件本身是通过 #include 被其他文件引用的,编译器会在最先引用该文件的文件夹内查找。
  3. 指定的项目包含路径(通过编译器参数指定的路径,例如 -I 选项):
    • 编译器会查找通过编译器命令行参数指定的头文件路径(-I/path/to/include)。这个路径可以是多个目录,依赖于构建系统的设置。
  4. 全局系统路径
    • 如果前面的路径中没有找到文件,编译器会在全局头文件目录中查找。通常这些路径是操作系统或者 IDE 预先配置好的系统路径,比如 /usr/include(Linux 系统)或者 C:\Program Files\(Windows 系统)。
尖括号 <> 的搜索路径机制

当使用尖括号 #include <filename> 包含头文件时,编译器仅在全局或系统路径中查找头文件,忽略当前目录。这是为了更高效地查找标准库和第三方库中的头文件。查找路径如下:

  1. 标准库路径

    • 编译器首先在 C++ 标准库的路径中查找头文件。这些路径可能包含 <iostream>, <vector>, <string> 等标准库文件。
  2. 系统库路径

    • 然后,编译器会查找系统头文件路径。比如,在 Linux 系统中,常见路径为 /usr/include/usr/local/include,而在 Windows 系统中,可能是 C:\Program Files (x86)\Microsoft Visual Studio\
  3. 指定的头文件路径

    • 依赖于编译器设置,可能还会根据用户指定的第三方库路径(如通过 -I 指定的路径)来查找第三方库的头文件,例如 Boost、Qt 等。
双引号和尖括号的选择与差异
  • 使用双引号 "" 更适合引用 项目内部 或者 本地定义的头文件,因为它会首先查找当前文件目录,确保本地头文件的优先级较高。这种方式适用于项目间的模块化开发。
  • 使用尖括号 <> 通常用于 标准库外部库 的头文件,避免在当前目录下无意中引用到同名的头文件。
更详细的搜索过程比较
使用符号查找顺序适用场景
双引号 ""1. 当前目录
2. 引用文件所在目录
3. 编译器指定的项目路径(如 -I
4. 系统路径
用于引用本地的头文件
尖括号 <>1. 标准库路径
2. 系统路径
3. 第三方路径
用于标准库和外部库的头文件
编译器选项与头文件路径

在使用编译器时,可以通过选项配置头文件的查找路径。例如:

  • GCC/Clang

    • -I:指定额外的头文件路径。例如,g++ -I/path/to/headers main.cpp 会告诉编译器在 /path/to/headers 中查找头文件。
  • Visual Studio

    • 在项目属性的 "C/C++" > "附加包含目录" 中,可以指定额外的包含目录。编译器会在这些目录中查找双引号 "" 和尖括号 <> 所包含的头文件。
  • CMake

    • 使用 include_directories()target_include_directories() 指定头文件的搜索路径。

2. 双引号和尖括号的深层机制

双引号和尖括号的主要区别在于它们告诉编译器如何查找文件的路径。尖括号中的头文件往往是一些不常修改的外部依赖,比如标准库文件、系统级文件或者第三方库,而双引号中的头文件是用户自定义的文件,更容易发生变化。因此,双引号和尖括号的查找路径和优先级设计不同。

双引号 vs 尖括号查找行为

双引号 "filename":
1. 查找当前目录/源文件目录
2. 查找编译器路径(通过 -I 指定)
3. 查找标准系统路径

尖括号 <filename>:
1. 查找标准系统路径
2. 查找第三方路径

3. #include 的例子与实践

包含自定义头文件的实例

#include "MyClass.h" // 当前目录查找
#include "subfolder/Helper.h" // 指定子文件夹查找

#include <iostream> // 系统库查找,标准C++库
#include <cmath>    // 系统路径下的数学库

在这个例子中,"MyClass.h""subfolder/Helper.h" 都是项目内的文件,编译器会从源文件所在的目录或项目结构中的路径查找。<iostream><cmath> 是标准库的头文件,因此编译器直接在标准路径中查找。

4. 如何正确组织头文件

为了避免潜在的查找冲突和提高编译速度,建议遵循以下头文件组织原则:

  1. 使用前向声明

    • 尽量在头文件中使用前向声明来减少包含不必要的头文件。例如,在类成员中引用其他类的指针时,可以通过前向声明避免引入整个类的定义。

                class MyClass; // 前向声明
                class AnotherClass {
                    MyClass* ptr; // 使用指针或引用即可避免包含整个 MyClass 头文件
                };

        

      2.防止多次包含(Include Guard)

  • 为了避免头文件的多次包含,常用的做法是使用包含防护,即 #ifndef#define#endif 组合,或者在现代 C++ 中使用 #pragma once
  • // MyClass.h
    #ifndef MYCLASS_H
    #define MYCLASS_H

    class MyClass {
        // Class Definition
    };

    #endif // MYCLASS_H

  1. 确保头文件独立

    • 每个头文件应该可以独立包含,不依赖其他头文件的顺序。通过包含防护和适当的前向声明,避免头文件之间的复杂依赖关系。

5. 总结

  • 双引号 "":优先用于包含项目中的本地文件,编译器会先从当前目录查找,再到系统路径查找。
  • 尖括号 <>:用于包含标准库或外部库文件,编译器直接从系统路径查找。
  • 头文件包含顺序:优先包含与当前实现文件相关的头文件,然后是标准库头文件,接着是第三方库,最后是项目内部其他模块的头文件。


http://www.ppmy.cn/devtools/125407.html

相关文章

Git 工作区、暂存区和仓库

在使用 Git 进行版本控制时&#xff0c;工作区、暂存区和仓库概念的详细解释&#xff1a; 1. 工作区&#xff08;Working Directory&#xff09; 工作区是你在计算机上实际编辑文件的地方。当你克隆一个 Git 仓库或在现有目录中初始化一个 Git 仓库时&#xff0c;这个目录就是…

vue 请求竞态 中断请求 解决切换表格数据,数据发生错乱

//1&#xff0c;声明缓存请求的集合 const pendingRequest new Map(); //2,请求url和method生成key const generateRequestKey <T extends AxiosRequestConfig>(config: T) > {const { method, url } configreturn [method, url].join("&") } //3,缓…

【Android】在安卓中使用 `mobile-ffmpeg` 压缩后的视频,浏览器在线播放提示“没有找到支持的视频格式和 MIME 类型”的解决方案

在安卓中使用 mobile-ffmpeg 压缩后的视频&#xff0c;浏览器在线播放提示“没有找到支持的视频格式和 MIME 类型”的解决方案 你可能在安卓开发中使用了 mobile-ffmpeg 进行视频压缩&#xff0c;而当你尝试在浏览器中在线播放压缩后的视频时&#xff0c;看到提示&#xff1a;…

【算法思想·二叉树】用「遍历」思维解题 II

本文参考labuladongsuanfa笔记[【强化练习】用「遍历」思维解题 II | labuladong 的算法笔记] 如果让你在二叉树中的某些节点上做文章&#xff0c;一般来说也可以直接用遍历的思维模式。 270. 最接近的二叉搜索树值 | 力扣 | LeetCode | 给你二叉搜索树的根节点 root 和一个目…

python:假的身份信息生成模块faker

前言 发现一个有趣的python模块&#xff08;faker&#xff09;&#xff0c;他支持生成多个国家语言下的假身份信息&#xff0c;包含人名、地址、邮箱、公司名、电话号码、甚至是个人简历&#xff01; 你可以拿它做一些自动化测试&#xff0c;或一些跟假数据有关的填充工作。 代…

机器学习笔记(五)--神经网络

神经元网络&#xff1a;M-P神经元模型 神经元接收来自其他n个神经元传递过来的信号&#xff0c;这些信号通过有权重的连接进行传递&#xff0c;神经元接收到的总输入值将与神经元的阈值进行对比&#xff0c;通过激活函数的处理产生神经元的输出。 感知机与多层网络 感知机&a…

如何恢复笔记本电脑上误删除的谷歌浏览器数据

在使用笔记本电脑的过程中&#xff0c;有时我们可能会不小心删除了重要的谷歌浏览器数据&#xff0c;如书签、历史记录或保存的密码。本文将详细介绍如何在笔记本上恢复这些误删除的数据&#xff0c;帮助你找回丢失的信息。&#xff08;本文由https://www.gugeliulanqi.com.cn/…

java web 之过滤器Filter

1、概念 当访问服务器的资源时&#xff0c;Filter过滤器可以将请求拦截下来&#xff0c;完成一些特殊的功能。 通常都是用来拦截request进行处理的&#xff0c;也可以对返回的response进行拦截处理。 一般用于完成通用的操作。如&#xff1a;登录验证、统一编码处理、敏感字…