01、文章目录
文章目录
02、浅谈条件编译
条件编译是指预处理器根据条件编译指令,有条件地选择源程序代码中的一部分代码作为输出,送给编译器进行编译。主要是为了有选择性地执行相应操作,防止宏替换内容(如文件等)的重复包含。
常见的条件编译指令如下:
条件编译指令 | 描述(Description) |
---|---|
#if | 如果条件为真,则执行相应操作 |
#elif | 如果前面条件为假,而该条件为真,则执行相应操作 |
#else | 如果前面条件均为假,则执行相应操作 |
#endif | 结束相应的条件编译指令 |
#ifdef | 如果该宏已定义,则执行相应操作 |
#ifndef | 如果该宏没有定义,则执行相应操作 |
#define | 定义一个宏 |
#undef | 取消一个宏定义 |
严格来说,#define与undef不能算是真正的条件编译,虽然他也是在编译阶段完成,下面介绍几组常见的条件编译组合。
03、各版本条件编译
3.1 #if——#else——#endif
- 调用格式:
#if 条件表达式
程序段1
#else
程序段2
#endif
- 功能:如果#if后的条件表达式为真,则程序段 1 被选中,否则程序段 2 被选中。
注意,必须使用 #endif 结束该条件编译指令。 - 示例:
#include <iostream>
using namespace std;
int main()
{
int a,b,c;
a = 10;
b = 20;
c = 30;
#if 1
printf("a+b = %d",a+b);
#else
printf("b+c = %d",b+c);
#endif
system("pause");
return 0;
}
上述这段简单的程序,当#if旁边为TRUE,为真时,执行#if 下面的语句块直到#else之上的所有。
当#if旁边为FALSE,为假时,执行#else下面的语句块,直到#endif之上的所有。
3.2 #ifndef——#define——#endif
- 调用格式:
#ifndef 标识符
#define 标识符 替换列表
//...
#endif
- 功能:一般用于检测程序中是否已经定义了名字为某标识符的宏,如果没有定义该宏,则定义该宏,并选中从 #define 开始到 #endif 之间的程序段;如果已定义,则不再重复定义该符号,且相应程序段不被选中。
- 示例
#include <iostream>
using namespace std;
#ifndef MAX //检查是否已定义
#define MAX 1024 //没有定义就使用该行
#endif //结束标识
int main()
{
system("pause");
return 0;
}
该条件编译指令更重要的一个应用是防止头文件重复包含。
如果 f.cpp 源文件中包含 f1.h 和 f2.h 两个头文件,而 f1.h 头文件及 f2.h 头文件中均包含 x.h 头文件,则 f.cpp 源文件中重复包含 x.h 头文件。可采用条件编译指令,来避免头文件的重复包含问题。所有头文件中都按如下格式:
#ifndef _HEADNAME_H_
#define _HEADNAME_H_
//头文件内容
#endif
Code解释:当该头文件第一次被包含时,由于没检测到该头文件名对应的符号(宏名)HEADNAME_H,则定义该头文件名对应的符号(宏),其值为该系统默认。并且,该条件编译指令选中 #endif 之前的头文件内容;如果该头文件再次被包含时,由于检测到已存在以该头文件名对应的符号(宏名),则忽略该条件编译指令之间的所有代码,从而避免了重复包含。
3.3 #if——#elif——#else——endif
该条件编译即是第一种的升级版——puls版本
- 调用格式
#if 条件表达式1
程序段 1
#elif 条件表达式2
程序段 2
#else
程序段3
#endif
-
功能:先判断条件1的值,如果为真,则程序段 1 被选中编译;如果为假,而条件表达式 2 的值为真,则程序段 2 被选中编译;其他情况,程序段 3 被选中编译。
-
示例:
#include <iostream>
using namespace std;
int main()
{
int a,b,c;
a = 10;
b = 20;
c = 30;
#if 0
printf("a+b = %d",a+b);
#elif 1
printf("a+c = %d",a+c);
#else
printf("b+c = %d",b+c);
#endif
system("pause");
return 0;
}
上述程序就是第一个的升级版,想必,不难理解,就是判断两次,第一次为1,就不往下走,第一次若是为0,则判断第二次,同上规则。
3.4 #ifdef——#endif
- 调用格式
#ifdef 标识符
程序段
#endif
-
功能:如果检测到已定义该标识符,则选择执行相应程序段被选中编译;否则,该程序段会被忽略。
-
示例:
#ifdef N
#undef N
//程序段
#endif
Code解释:如果检测到符号 N 已定义,则删除其定义,并选中相应的程序段。
04、宏定义
4.1 定义宏(#define)
定义宏分为两种:
第一种:对象宏(宏常量定义)
第二种:函数宏
- 对象宏
不带参数的宏被称为"对象宏(objectlike macro)"。对象宏多用于定义常量、通用标识。
例如:
// 常量定义
#define MAX_LENGTH 100
// 通用标识,日志输出宏
#define SLog printf
// 预编译宏
#define _DEBUG
- 函数宏
带参数的宏。利用宏可以提高代码的运行效率: 子程序的调用需要压栈出栈, 这一过程如果过于频繁会耗费掉大量的CPU运算资源。 所以一些代码量小但运行频繁的代码如果采用带参数宏来实现会提高代码的运行效率。但多数c++程序不推荐使用函数宏,调试上有一定难度,可考虑使用c++的inline代替之。
例如:
// 最小值函数
#define MIN(a,b) ((a)>(b)? (a):(b))
// 安全释放内存函数
#define SAFE_DELETE(p) {if(NULL!=p){delete p; p = NULL;}}
4.2 取消宏(#undef)
关于undef上面的例子中已经有了,只是没有说明,单一功能,删除当前宏。
这里就不多说,上面例子中有示例。
05、总结
当然宏的底层还可以讲的有很多,比如汇编剖析宏。由于本人的知识水平有限,暂时只能局限于上层使用了。如果有机会,有实力浅谈汇编的时候,再进一步聊聊宏的汇编。
版权声明:转载请注明出处,谢谢!