C++学习笔记----10、模块、头文件及各种主题(二)---- 预处理指令

使用#include预处理指令来包含头文件的内容。还有一些预处理指令。下面列表展示了一些常用的预处理指令:

|------------------------------------------------------|------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| 预处理指令 | 功能 | 通用场景 |
| #include [file] | [file]文件名的内容插入到指令位置的代码中 | 几乎总是用于包含头文件以便代码可以使用定义在其它地方的功能 |
| #define [id] [value] | 每个标识符[id]出现的地方都用[value]替换 | 常用于C定义常数或宏。c++对于常数与大部分类型的宏提供了更好的方法。宏可能会危险,所以要小心使用。 |
| #undef [id] | 使前面使用#define定义的[id]失效 | 如果定义的标识符只要求在限定范围的代码中使用。 |
| #if [expression] #elif [expression] #else #endif | 基于给定表达式的结果有条件包含代码块 | 常用于提供特定平台的特定代码 |
| #ifdef [id] #endif #ifndef [id] #endif | 基于特定标识符是否使用#define定义有条件地包含代码。#ifdef [id]与#if defined(id)等价,#ifndef [id]与#if !defined(id)等价 | 最常用于保护循环包含。以#ifndef开头的头文件检查是否有标识符的定义,接着用#define指令定义该标识符。头文件以#endif结束。这防止文件被包含多次。 |
| #elifdef [id] #elifndef [id] | #elifdef [id]与#elif defined(id)等价,#elifndef [id]与#elif !defined(id)等价 | 其它功能的缩写。 |
| #pragma [xyz] | 控制特定编译器的行为。[xyz]是编译器依赖的。大部分编译器支持once来防止头文件包含多次 | |
| #error [message] | 使编译以给定的信息停止。 | 如果用户想在不支持的平台上编译代码,可用于停止编译。 |
| #warning [message] | 使编译器发出给定警告信息,但是编译继续。 | 在不影响编译结果的情况下用于显示警告给用户。 |

1、预处理宏

可以使用C++预处理指令书写宏,看起来像小函数。举例如下:

cpp 复制代码
#define SQUARE(x) ((x) * (x)) // No semicolon after the macro definition!

int main()
{
    println("{}", SQUARE(5));
}

宏是C的产物,与inline函数很像,但是不进行类型检查,预处理只是单纯地将对其的调用进行替换。预处理器不执行真实的函数调用语法检查。这种行为可能产生不可预知的结果。例如,考虑如果用2+3而不是5来调用SQUARE宏会发生什么,像这样:

cpp 复制代码
println("{}", SQUARE(2 + 3));

你想让SQUARE计算出25,它确实这么做了。然而,如果你漏掉了宏定义中的一些括号,看起来像这样?

cpp 复制代码
#define SQUARE(x) (x * x)

现在对SQUARE(2+3)的调用生成了11,而不是25!记住宏只是单纯地替换而不进行函数调用语法的检查。这就意味着宏体中的x被替换为了2+3,变成如下的语句:

cpp 复制代码
println("{}", (2 + 3 * 2 + 3));

遵守运算的优先级顺序,这行代码行执行乘法,接着是加法,生成11而不是25!

宏也有性能影响。假定调用SQUARE宏如下:

cpp 复制代码
println("{}", SQUARE(veryExpensiveFunctionCallToComputeNumber()));

预处理器替换如下:

cpp 复制代码
println("{}", ((veryExpensiveFunctionCallToComputeNumber()) *
(veryExpensiveFunctionCallToComputeNumber())));

现在调用了昂贵的函数两次--另一个避免使用宏的原因。

宏在排错时也会出问题,因为你写的代码不是编译器看到的代码,或者说是在debugger中显示的代码(因为预处理器的查找替换行为)。由于这些原因,应该避免使用宏,要使用内联函数。讲这么多就是因为确实有一些代码还在使用宏。需要理解、阅读并维护这样的代码。

注意:大部分编译器可以输出预处理源码到文件或标准输出。可以使用这个我来观察预处理器怎么预处理你的代码。例如,在微软Visual C++中可以使用/P开关。GCC中使用-E开关。

相关推荐
Yurko138 分钟前
【计网】基于三层交换机和 RIP 协议的局域网组建
网络·学习·计算机网络·智能路由器
静若繁花_jingjing11 分钟前
DDD领域驱动设计实践_保险
java·开发语言
lijun_xiao200913 分钟前
Dubbo-学习笔记1
笔记·学习
程序猿202314 分钟前
Python每日一练---第十二天:验证回文串
开发语言·python
wjs202416 分钟前
AJAX 实例详解
开发语言
我要升天!19 分钟前
QT -- 初识
开发语言·qt
卡提西亚20 分钟前
C++笔记-23-类和对象-多态
c++·笔记
奋斗的牛马20 分钟前
硬件工程师-基础知识电阻(四)
单片机·嵌入式硬件·学习·fpga开发
楼田莉子21 分钟前
Linux学习:进程的控制
linux·运维·服务器·c语言·后端·学习
梁小憨憨25 分钟前
Psychtoolbox (PTB)安装指南
笔记