【#ifndef, #define, 和 #endif】

前言

学习AFNetWoring源码的时候,在AFN的h借接口文件又看到了这几个宏定义,学习记录一下。
请添加图片描述

作用

#ifndef, #define, 和 #endif是C/CPP的预处理指令,常常用来条件编译和防止头文件重复包含。

简介

#ifndef 它是if not define的简写,是宏定义的一种,确切的说是预处理功能(宏定义、文件包含、条件编译)三种中的一种——条件编译。

使用#ifndef可以避免以下错误:如果在.h文件中定义了全局变量,一个C文件包含了.h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef则不会出现这种错误。

  • #ifndef:如果未定义)用于判断指定的标识符是否已经被定义。如果该标识符未定义,则执行下面的代码块,否则跳过。
  • #define: 用于定义指定的标识符,通常用于定义宏。在条件编译中,一般用来定义一个标识符,以便在后面的代码中进行条件判断。
  • #endif :用于结束条件编译的代码块。

通常语法如下

#ifndef IDENTIFIER
#define IDENTIFIER

// 在这里放置需要执行的代码

#endif

其中,IDENTIFIER 是你自己定义的标识符名称,可以是任意合法的标识符。

这种结构的作用是为了防止头文件的重复包含。通过定义一个唯一的标识符,当该头文件第一次被包含时,IDENTIFIER 未定义,预处理器会执行 #define 下的代码块,标识该头文件已经被包含。当同一个头文件再次被包含时,由于 IDENTIFIER 已经被定义,预处理器会跳过 #define 下的代码块,从而避免了重复包含。

这种写法在头文件中很常见,以确保头文件的内容只会被编译一次,防止重复定义和冲突。

例子

以开头的代码为例

#ifndef _AFNETWORKING_
    #define _AFNETWORKING_

    #import "AFURLRequestSerialization.h"
    #import "AFURLResponseSerialization.h"
    #import "AFSecurityPolicy.h"

#if !TARGET_OS_WATCH
    #import "AFNetworkReachabilityManager.h"
#endif

    #import "AFURLSessionManager.h"
    #import "AFHTTPSessionManager.h"

#endif /* _AFNETWORKING_ */

这段代码的是OC的预编译指令,用于编译时对于代码进行条件判断和控制

  • #ifndef:预处理指令,用于判断一个标识符是否已经被定义过。如果标识符没有被定义过,则编译下面的代码块,否则忽略下面的代码块。
  • #define:预处理指令,用于定义一个标识符。在这个例子中,AFNETWORKING 就是被定义的标识符。
  • #endif:预处理指令,用于结束一个条件编译块。
    这段代码的作用是:当 AFNETWORKING 这个标识符没有被定义时,编译下面的代码块,并且将 AFNETWORKING 定义为已经被定义。这样可以避免头文件的重复引用和编译错误。

分解

#ifndef _AFNETWORKING_
#define _AFNETWORKING_

这里定义了一个标识符 _AFNETWORKING_,如果该标识符未定义(即未被之前的代码定义过),则执行下面的代码块,否则跳过。

#import "AFURLRequestSerialization.h"
#import "AFURLResponseSerialization.h"
#import "AFSecurityPolicy.h"

这里通过#import指令引入了一些其他的头文件,这些头文件可能包含了一些类或函数的声明。

#if !TARGET_OS_WATCH
#import "AFNetworkReachabilityManager.h"
#endif

这段代码使用了条件编译,判断是否为目标平台是不是watchOS,如果不是watchOS,则引入 "AFNetworkReachabilityManager.h" 头文件。

#import "AFURLSessionManager.h"
#import "AFHTTPSessionManager.h"

这里又引入了两个头文件 "AFURLSessionManager.h""AFHTTPSessionManager.h"

最后,代码块的结束部分是 #endif,用于结束条件编译的代码块。

整个结构的目的是为了确保头文件的内容只会被编译一次,避免了重复定义和冲突。

<think>好的,用户想了解C或C++中的#ifndef#else的用法及相关示例。首先,我需要回顾一下预处理指令的基本概念,特别是条件编译部分。 #ifndef是“if not defined”的缩写,用于检查某个宏是否未被定义。如果该宏未被定义,则编译其后的代码,直到遇到#endif、#else或#elif。这通常用于防止头文件被多次包含,避免重复定义的问题。例如,在头文件的开头使用#ifndef HEADER_H,然后定义HEADER_H,这样在多次包含时,只有第一次会生效,后续的包含会被跳过。 接下来是#else,它普通的if-else结构类似,当#ifndef的条件不满足时,编译器会处理#else之后的代码。例如,可以根据不同的平台定义不同的宏,当某个宏未被定义时,使用#else分支来定义另一个宏。 需要举一些例子来说明。比如,头文件保护是最常见的应用场景。另外,不同平台的代码适配,调试模式开关,功能模块的启用或禁用也是常见的用例。需要注意的是,每个条件编译块都必须以#endif结束,否则会导致编译错误。 用户可能需要了解这些预处理指令的实际应用,以及如何避免常见错误。例如,忘记#endif,或者宏命名冲突的问题。此外,可能还需要解释与#pragma once的区别,虽然两者都用于防止重复包含,但#pragma once是非标准的,可能在某些编译器上不被支持,而#ifndef是标准方法,兼容性更好。 在生成相关问题的时候,可以考虑条件编译的其他指令,比如#ifdef、#elif的用法,或者条件编译的实际应用案例,比如跨平台开发中的使用。此外,常见错误调试技巧也是用户可能感兴趣的部分。 需要确保回答结构清晰,分点说明,并给出代码示例。同时,参考提供的引用材料,虽然引用中的内容主要涉及C扩展、多线程TensorRT部署,可能与当前问题关联不大,但如果有相关点可以联系的话需要提及,比如在多线程代码中可能使用条件编译来区分不同平台的具体实现,但可能用户提供的引用中没有直接相关的内容,所以主要专注于用户的问题本身。 最后,检查是否有语法错误,确保所有的例子中的预处理指令正确使用,并符合C/C++的标准。确保使用中文回答,行内公式用$...$,独立公式用$$...$$,但这个问题中可能不需要数学公式,所以可能不涉及。代码块使用正确的语法高亮,比如C或C++的代码示例。</think>### C/C++#ifndef与#else的用法解析 在C/C++中,`#ifndef``#else`是**预处理指令**,主要用于**条件编译**。它们通过判断宏定义的状态,决定哪些代码需要被编译器处理。以下是具体用法示例: --- #### 一、基本语法与功能 1. **`#ifndef`(if not defined)** - 检查某个宏是否**未被定义**,若未定义则编译其后的代码块。 - 常用于**头文件保护**(防止重复包含)或**平台适配**。 ```c #ifndef MACRO_NAME // 代码块1(当MACRO_NAME未定义时生效) #else // 代码块2(当MACRO_NAME已定义时生效) #endif ``` 2. **`#else`** - 与`#ifndef`或`#ifdef`配对使用,表示条件不满足时的分支。 --- #### 二、典型应用场景 ##### 1. 头文件保护(Header Guard) 防止头文件被多次包含导致的重复定义问题[^1]: ```c // example.h #ifndef EXAMPLE_H // 检查EXAMPLE_H是否未定义 #define EXAMPLE_H // 定义EXAMPLE_H void example_function(); #endif // 结束条件编译块 ``` ##### 2. 跨平台代码适配 根据不同的操作系统选择不同实现: ```c #ifndef WINDOWS_PLATFORM // Linux/Mac平台的代码 #include <unistd.h> #else // Windows平台的代码 #include <windows.h> #endif ``` ##### 3. 调试模式开关 通过宏控制调试信息的输出: ```c #ifndef DEBUG_MODE #define LOG(msg) // 空实现(不输出日志) #else #define LOG(msg) std::cout << "[DEBUG] " << msg << std::endl #endif ``` ##### 4. 功能模块的启用/禁用 动态配置程序功能: ```c #ifndef USE_FEATURE_A // 未启用Feature A时的替代代码 #else // Feature A的具体实现 #endif ``` --- #### 三、注意事项 1. **必须配对使用** 每个`#ifndef`必须对应一个`#endif`,否则会导致编译错误。 ```c #ifndef MACRO // 代码块 #endif // 必须有此行 ``` 2. **宏命名的唯一性** 头文件保护宏应使用**全局唯一名称**(通常为头文件名的大写形式,如`EXAMPLE_H`)。 3. **与`#pragma once`的区别** - `#pragma once`是编译器扩展,非C/C++标准,但现代编译器普遍支持。 - `#ifndef`是标准方法,兼容性更好。 --- #### 四、综合示例 ```c #include <iostream> // 根据平台选择不同的库 #ifndef USE_OPENGL #include "vulkan_api.h" // 使用Vulkan库 #else #include "opengl_api.h" // 使用OpenGL库 #endif int main() { #ifndef DEBUG std::cout << "Release Mode" << std::endl; #else std::cout << "Debug Mode" << std::endl; #endif return 0; } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值