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开关。

相关推荐
学会去珍惜1 分钟前
C语言简介
c语言·开发语言
思麟呀4 分钟前
C++11 核心特性(三):强类型枚举、static_assert 与 std::tuple
开发语言·c++
hoiii18710 分钟前
Qt 实现屏幕截图功能
开发语言·qt·命令模式
一拳一个呆瓜16 分钟前
【STL】C++程序的启动与终止
c++·stl
05大叔21 分钟前
对话系统学习,问答型数据库,闲聊型对话数据库
学习
nashane21 分钟前
HarmonyOS 6商城开发学习:抢票倒计时与系统日历提醒——票务类场景的完整落地思路
学习·华为·harmonyos
小白学大数据29 分钟前
爬虫性能天花板:asyncio赋能 Aiohttp,并发提速 10 倍
开发语言·爬虫·数据分析
凡人叶枫40 分钟前
Effective C++ 条款07:为多态基类声明 virtual 析构函数
linux·c语言·开发语言·c++
凡人叶枫1 小时前
Effective C++ 条款10:令 operator= 返回一个 reference to *this
java·linux·服务器·开发语言·c++·effective c++