一、迅速理解#ifdef/#ifndef
这两个是条件编译指令,核心:根据「宏是否定义」决定要不要编译某段代码,类比 "if 判断"。
| 指令 | 含义 | 类比普通代码 |
|---|---|---|
#ifdef A #else |
如果宏 A 被定义过,就编译后续代码 | if (A已定义) { ... } |
#ifndef A #else |
如果宏 A 没被定义过,就编译后续代码 | if (A未定义) { ... } |
#endif |
结束条件编译(必须配对) | } |
简单来说条件语句有两个: #ifdef 和 #ifndef 如果定义了某个宏 如果没有定义某个宏?
要注意每一个条件语句都要有对应的结束匹配语句: #endif
1. 基础用法示例( 2 个场景)
场景 1:区分调试 / 发布模式(最常用)
运行 : 简单说 为什么编译器可以通过宏来确定,这个编译器做的处理之后我会用gcc编译器举例来说 gcc 可以带上宏选项
// 第一步:定义/注释宏,控制逻辑
#define DEBUG // 取消这行就是发布模式 debug理解为调试模式
// 第二步:条件编译
#include <stdio.h>
int main() {
#ifdef DEBUG // 如果定义了DEBUG,执行调试代码
printf("调试模式:打印变量值\n"); // 发布时自动不编译这行
#else
printf("发布模式:不打印调试信息\n");
#endif
return 0;
}
像我们使用的IDE,可视化界面上有
模式的选择,本质上就是在编译的时候,会带上对应的宏定义之后代码就会进行选择性的执行了
场景 2:防止头文件重复包含(#ifndef核心用法)
运行
cpp
// 头文件 test.h 开头
#ifndef TEST_H // 如果TEST_H没定义
#define TEST_H // 立刻定义它,防止重复
// 头文件的核心内容(函数、宏、结构体等)
void func();
#endif // 结束条件编译
(这样多次#include "test.h"时,只有第一次会编译内容,避免重复定义错误)
二、C++11 下的NULL定义的代码含义以及nullptr的引入
2.1 C/C++下空指针: NULL是什么
cpp
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
这段代码的意思: 条件编译: 如果NULL宏不存在,就执行下面语句: 如果是C++编译器 (__cplusplus 是c++编译器会加上的选项 )就执行 :
C++ 编译环境---》 让NULL就是0
C语言 编译环境下 ---》 NULL是 void*
2.2 C++为什么NULL为0&&nullptr哪来的

1. c语言弱类型属性 void*
cpp
#include<stdio.h>
int main() {
int* p1 = NULL;
int* p2 = (void*)0;
void* p = p2;
int* pa = p; //
char* pc = p;// 发现 void*可以转换为其他任意任意的指针: int* char*
return 0;
}
void* 可以赋值给 任意具体类型的指针
2. C++11 提出对类型的强制要求
- c++是强类型匹配 2. NULL会出现重载的小问题所以有nullptr

在c++中不允许void*类型赋值给具体类型的指针 因为C++类型的强类型匹配规则,
所以必须显式的写强转 : 如上面代码 int*pi = (int*) p p是一个void*类型
其次为什么 int* pi =NULL 因为0是唯一一个可以直接表示空指针的整数,所以用0啊
所以在C++中空指针本质上是用0来表示了,但是它有一个缺点就是 C++函数重载的时候:
如下:
cpp
// 整数重载
void func(int a){;}
// 指针重载
void func(int* a){;}
当我们调用函数 func(NULL)的时候 会优先匹配int的func这就有点彻底,我的NULL就是想表示空指针啊,你太过分了 。
所以引入了nullptr
nullptr
C++引入nullptr的原因就是因为想表示这就是一个空指针常量,注意我的描述它是常量不是宏就是一个指针常量而且是空指针 ,所以C++到这类型就得到一个闭环了。
3. c接口很多都是func(void* a)万能类型参数还能用吗 ?
答案是完全可用 : 如下表来参考这个关系:
|-----------------------------------------------------|---|
| | |
| int * = void* (这是不可以接受的赋值 类型不匹配c++不允许 ) | |
| void* = int* ; (这是可用接受的 因为void* 可用理解为空类型 或者万能类型) | |
没有报错:

如linux中 pthread的线程库就很多void*参数的接口



模式的选择,本质上就是在编译的时候,会带上对应的宏定义之后代码就会进行选择性的执行了