文章目录
一、#define 详解
1. #define 定义标识符
#define name stuff
本质:将代码中的所有
name替换成stuff。
如果定义的 stuff 过长,可以分成几行写。
但是除了最后一行,每行后面都要加一个 \(续行符) 。
比如:
2. #define 定义宏
利用 #define 把参数替换到文本中,这种实现称为宏。
#define name( parament-list ) stuff
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
3. #undef 用法
该指令用于移除 #define 的定义。
比如:
#define N 10
#undef N
4. #define 易错点
下面提问:在 #define 定义标识符的时候,要不要在最后加上
;。
答:建议不要,容易出现问题。
比如下面的场景:
这里的MAX在预处理阶段替换成1000;。
所以,i = MAX;预处理后就变成了i = 1000;;。
这样多了一个;,表示在 if 语句后面的一个空语句,导致悬空 else。#define 的本质是替换,所以不会先进行计算。
比如:
如上图所示,输出结果是5*2+3=13,而非5*5=25。
因此,为了保证使用不出错,建议多加括号。
特别是宏,每个都加,整体再加。
比如:
#define DOUBLE( x) ( ( x ) + ( x ) )
5. #define 替换规则
- 调用宏时,如果宏中有已经被定义的标识符,要先替换。
- 宏不能递归。
- 预处理器查找 #define 定义的符号时,不会查找字符串里的内容。
6. 宏与函数对比
先说结论:对于简单的运算,用宏更好。
比如求两个数中的较大值:
#define MAX(a, b) ((a)>(b)?(a):(b))
- 宏的优点:
- 更快。
- 函数存在调用和返回的额外开销。
- 宏的参数与类型无关,更自由。
- 比如,宏的参数可以是类型:

- 比如,宏的参数可以是类型:
- 宏的缺点:
- 宏无法调试。
- 宏与类型无关,不够严谨。
- 宏可能会有运算符优先级的问题,容易出错。
- 比如传入++a这样的内容。
命名规定:
- 宏全部大写。
- 函数名不要全部大写。
二、条件编译
条件编译,即条件为真则编译,否则跳过不编译。
常见的条件编译指令:
#if 常量表达式
//...
#endif
#if和#endif是一对,成对出现。
举个例子
此时条件为真,屏幕上成功打印出内容。

此时条件为假,在VS中,main函数失去高光,不编译。
条件编译还可以有多个分支。
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
除了上面的指令之外,条件编译还可以判断某符号是否被#define定义。
#ifdef N
//..
#endif
#ifndef N
//..
#endif
#ifdef和#endif也是成对出现的。
上述代码中,如果N被定义,则#ifdef判断条件为真,否则为假。
#ifndef恰好相反。
还有一种写法,与之完全等价。
#if defined(N)//#ifdef N
//...
#endif
#if !defined(N)//#ifndef N
//...
#endif
三、文件包含
库文件包含:直接去标准路径查找。
#include <filename.h>
本地文件包含:先在源文件所在目录查找,没找到再去标准路径查找。
#include "filename"
于是我们发现,库文件似乎也可以使用 "" 的形式包含。
但是这样做查找的效率就会低一些,因为要先在源文件所在目录查找,然后没找到,再去标准路径查找。而且,这样也区分不了是库文件还是本地文件,代码可读性较低。
四、实用小技巧
在写项目的时候,我们有时会不可避免地重复包含头文件,这样就导致同一个头文件进行了多次编译,造成资源浪费。
事实上,我们可以用条件编译的方式来避免多次编译:
#ifndef 头文件名
#define 头文件名
//头文件内容
#endif
另一种方法是避免头文件重复引入:
#pragma once