#ifndef #define #endif (条件编译) 和#pragma once

#ifndef #define #endif

作用与原理

  1. 防止重复包含
    当多个源文件包含同一个头文件,或头文件之间相互嵌套时,可能导致头文件内容被多次编译。使用这三条指令可以确保头文件内容仅被处理一次

  2. 实现机制

    • #ifndef(if not defined):检查某个标识符是否未定义。

    • #define:若标识符未定义,则定义它。

    • #endif:结束条件编译块。

    当首次包含头文件时,标识符未定义,预处理器会处理头文件内容并定义标识符。后续再次包含时,因标识符已定义,内容被跳过。

#ifndef XXXX

#define XXXX

#endif

字面意思

  • 如果宏XXXX未定义,则执行#ifndef XXXX#endif之间的代码(包括#define XXXX)。
  • 如果宏XXXX已定义,则跳过#ifndef XXXX#endif之间的代码。

示例 不用#ifndef #define #endif

a.h


#include"b.h"
#define A 1

b.h

#include "a.h"
#define B 2

main.cpp

#include<iostream>
#include"a.h"
#include"b.h"
int main()
{
    
    return 0;
}

编译运行 会报错

In file included from b.h:1:0,
                 from a.h:1,
                 from b.h:1,
                 ...... 
                 from b.h:1,
                 from a.h:1,
                 from main.cpp:2:
a.h:1:14: error: #include nested too deeply
 #include"b.h"
              ^
In file included from a.h:1:0,
                 from b.h:1,
                 from a.h:1,
                 .........
                 from b.h:1,
                 from a.h:1,
                 from b.h:1,
                 from main.cpp:3:
b.h:1:14: error: #include nested too deeply
 #include"a.h"

分析原因

main.cpp中包含了两个头文件a.h 和b.h

a.h中包含了头文件b.h

b.h中包含了头文件a.h

编译器预编译时候 就会形成一个死循环 a.h和b.h在一直循环引用 

造成nested too deeply(嵌套太深)

 示例 用#ifndef #define #endif

修改头文件

a.h

#ifndef __A_H_
#define __A_H_
#include"b.h"
#define A 1

#endif

b.h

#ifndef __B_H_
#define __B_H_
#include"a.h"
#define B 1
#endif

main.cpp

#include<iostream>
#include"a.h"
#include"b.h"
int main()
{
    return 0;
}

编译运行通过

原因分析

1 字面意思 #ifndef 如果没有定义后面的宏,执行2 。如果#ifndef 如果有定义后面的宏,执行3

2  #define 执行3

3 #endif

备注:#define 标识 应该是唯一的, 一般以头文件名字大写加上下划线和大写的H构成。

         一般头文件不要定义变量,只做声明(重复引用就会有重定义)

指令 #ifdef 和 #else #endif

#include<iostram>
#define TEST_CODE 
int main()
{
    int a 10;
#ifdef TEST_CODE 
    a =10;

#else
    a = 100;
#endif
    return 0;

}

#pragma once

核心作用

#pragma once 是 C/C++ 中的预处理指令,‌用于确保头文件在同一个编译单元(如 .cpp 文件)中仅被包含一次‌。它能防止因重复包含同一头文件导致的编译错误(如重复定义、类型冲突等)。


实现原理

  1. 编译器依赖
    #pragma once 由编译器直接处理(而非预处理器),通过记录头文件的物理路径或唯一标识符来检测重复包含。
    例如,若 header.h 被多次 #include,编译器会在首次包含后标记该文件,后续包含时直接跳。

  2. 文件唯一性判断
    编译器通过文件路径或哈希值判断头文件是否已加载。若路径相同(如绝对路径一致),则视为同一文件。
    例外情况:通过符号链接或不同相对路径引用同一文件时,可能失效。


 

 代码示例与实现

  1. 基本用法
    在头文件顶部添加 #pragma once

    // header.h 
    #pragma once 
    void exampleFunction();

#pragma once 和#ifndef...#define...#endif

差异比较

特性#pragma once#ifndef/#define 宏保护
实现方式编译器指令,基于文件路径唯一性预处理器宏,依赖唯一宏名
代码简洁性单行指令,无需手动命名需定义宏名,代码冗
编译效率某些编译器可优化(直接跳过文件需预处理器逐行判断宏是否定
兼容性主流编译器支持(MSVC、GCC≥3.4、Clang)符合 C/C++ 标准,通用性

‌‌注意事项

  1. 编译器兼容性
    现代编译器(如 MSVC、GCC≥3.4、Clang)均支持 #pragma once,但极少数旧版或特殊环境可能不支持。
    跨平台项目建议优先使用宏保护或混合方案

  2. 文件系统依赖
    若同一文件通过不同路径包含(如符号链接或拷贝文件),#pragma once 可能失效,此时宏保护更可靠

   2.1  符号链接导致 #pragma once 失效‌

        头文件 original.h 通过符号链接 link.h 被包含。
        代码中分别包含 #include "original.h" 和 #include "link.h"。
        ‌结果‌:编译器可能认为这是两个不同文件,导致 original.h 被重复包含,引发编译错误。

   2.2  ‌宏保护可靠性验证‌

        无论通过何种路径包含 original.h 或 link.h,只要头文件内定义相同宏名(如 MY_HEADER_H),预处理器仅允许一次展开。
        ‌结果‌:头文件内容仅被包含一次,避免重复定义问题。

‌技术总结‌

    ‌#pragma once 的局限性‌
        编译器可能无法识别同一文件的多个路径别名,导致保护失效。
        适用于‌文件路径唯一性明确‌的场景(如无符号链接、固定相对路径)。

    ‌#ifndef 的优势‌
        完全依赖宏名逻辑,与文件系统无关,可靠性更高。
        跨平台兼容性强,尤其适合需要处理符号链接或动态路径的项目


使用建议

  1. 优先选择 #pragma once

    • 适用于现代项目,尤其是基于主流编译器(如 VS、GCC、Clang)的环境。
    • 简化代码维护,减少宏命名冲突风险。
  2. 需兼容老旧编译器时选择 #ifndef

    • 若需支持不支持 #pragma once 的旧编译器(如早期嵌入式编译器)。
    • 跨平台项目中统一使用 #ifndef 可避免潜在兼容问题。
  3. 避免混合使用

    • 同时使用两种方式可能引发宏定义与编译器行为的冲突,增加调试难度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值