# 预处理器和宏 ## 宏 预处理在非预处理指令的所有行(即第一个非空白字符不是 # 的行)中展开宏,并在指令的某些部分(没有作为条件编译的一部分跳过)中展开宏。利用“条件编译”指令,您可以通过测试一个常量表达式或标识符取消对源文件的某些部分的编译,以确定将哪些文本块传递给编译器以及在预处理期间将从源文件中删除哪些文本块。 #define指令通常用于将有用标识符与常量、关键字和常用语句或表达式关联。表示常量的标识符有时称为“符号常量”或“清单常量”。表示语句或表达式的标识符称为“宏”。在该预处理器文档中,仅使用术语“宏”。 在程序源文本或某些其他预处理器命令的参数中识别宏的名称时,它将被视为对该宏的调用。宏名称将替换为宏主体的副本。如果宏接受参数,则宏名称后面的实参将替换为宏主体内的形参。将宏调用替换为已处理的主体副本的过程称为宏调用的“扩展”。 实际上,有两种类型的宏。“类似于对象的”宏不采用参数,但可以定义“类似于函数的”宏以接受参数,以便其外观和行为类似于函数调用。由于宏无法生成实际函数调用,因此您有时可以将函数调用替换为宏以使程序更快地运行(在C++中,内联函数通常是首选方法)。但是,如果未定义和小心使用宏,则宏会导致出现问题。必须在带有参数的宏定义中使用括号,以便在表达式中保持适当的优先级。此外,宏无法正确处理具有副作用的表达式。有关详细信息,请参阅#define指令中的getrandom示例。 一旦定义宏,则无法在未先删除原始定义的情况下将其重定义为不同的值。但是,您可以使用完全相同的定义来重定义宏。因此,相同的定义可在一个程序中出现多次。 #undef指令将删除宏的定义。一旦删除定义,就可以将该宏重定义为不同的值。#define指令和#undef指令是一组相对应的指令。 ## 宏和C++ C++提供了新功能,其中一些取代了ANSI C预处理器提供的功能。这些新功能可增强语言的类型安全性和可预见性: 1. 在C++中,声明为const的对象可用于常量表达式。这使程序可以声明具有类型和值信息的常量,以及可以用调试器以符号方式查看的枚举。使用预处理器#define指令定义常量不是那么精确。除非在程序中找到采用const对象的地址的表达式,否则不会为该对象分配存储区。 2. C++内联函数功能取代了函数类型宏。使用内联函数取代宏的好处如下: 类型安全。内联函数需要接受与常规函数相同的类型检查。宏不是类型安全。 纠正具有副作用的参数的处理。内联函数将计算在输入函数体前作为参数提供的表达式。因此,具有副作用的表达式不可能不安全。 ## 预定义的宏 Visual C+ +编译器预定义某些预处理器宏,具体取决于语言(C或C + +)、编译目标,以及选择的编译器选项。Visual c + + 支持 ANSI/ISO C99 标准和 ISO C + + 14 标准所指定的所需预定义的预处理器宏。该实现还支持几个更多特定于 Microsoft 的预处理器宏。仅针对特定的生成环境或编译器选项定义一些宏。除非另有说明,宏的定义整个翻译单元如同它们指定为 /D 编译器选项参数。在定义时,宏是由预处理器在编译前扩展为指定的值。预定义的宏不采用任何参数,并且不能重新定义。 ## 标准预定义的标识符 1. `__func__` 只在GCC version>=3.0的编译器中适用,多数情况下要用 `__FUNCTION__` 获取当前函数的名称,是封闭函数的未限定和未修饰名称 static const 数组 char。 ## 标准预定义的宏 1. `__cplusplus` 翻译单元将作为 c + + 编译时定义为整数文字值。否则,未定义。 2. `__DATE__` 当前源文件的编译日期。日期是一个固定长度的字符串文字的窗体Mmm dd yyyy。月份名称Mmm等同于中的缩写的月份名称生成的C运行库日期asctime函数。日期的第一个字符dd是一个空间,如果值是小于10。始终定义此宏。 3. `__FILE__` 当前源文件的名称。__FILE__扩展到字符的字符串文字。若要确保显示该文件的完整路径,请使用 /FC (完整路径的源代码文件中诊断程序)。始终定义此宏。 4. `__LINE__` 定义为当前源文件中整数行号。值__LINE__宏可通过使用更改#line指令。始终定义此宏。 5. `__STDC__` 定义为1,仅当作为 C 编译,如果 /Za 指定编译器选项。否则,未定义。 6. `__STDC_HOSTED__` 定义为1,如果实现是承载实现,属于支持整个所需的标准库的支持。否则,定义为 0。 7. `__STDCPP_THREADS__` 定义为1,当且仅当一个程序可以有多个线程的执行,并且编译为 c + +。 否则,未定义。 8. `__TIME__` 预处理过的翻译单元的转换的时间。时间是字符的字符串文字的窗体︰分︰秒,由 C 运行时库返回的时间相同 asctime 函数。始终定义此宏。 ## Microsoft专用预定义的宏 MSVC支持的预定义的宏。 1. `__ATOM__` 定义为 1 时 /favor:ATOM 设置编译器选项和编译器目标是 x86 或 x64。 否则,未定义。 2. `__AVX__` 定义为 1 时 /arch:AVX 或 /arch: avx2 可以 设置编译器选项和编译器目标是 x86 或 x64。 否则,未定义。 3. `__AVX2__` 定义为 1 时 /arch: avx2 可以 设置编译器选项和编译器目标是 x86 或 x64。 否则,未定义。 4. `_CHAR_UNSIGNED` 默认值定义为 1 如果 char 类型是无符号。 此值设置时 /J (默认 char 类型是无符号) 设置编译器选项。 否则,未定义。 5. `__CLR_VER` 定义为整数文字表示在已编译的应用程序时使用的公共语言运行时版本。 在窗体中编码的值 Mmmbbbbb, ,其中 M 是运行时,主要版本 mm 是运行时的次要版本和 bbbbb 为内部版本号。 __CLR_VER 如果定义 /clr 设置编译器选项。 否则,未定义。 6. `_CONTROL_FLOW_GUARD` 定义为 1 时 /guard:cf (启用控制流防护) 设置编译器选项。 否则,未定义。 7. `__COUNTER_Expands` 为整数文字,从 0 开始的和每次在源文件中使用,就会递增 1,或者包含的源文件标头。 COUNTER 会记住其状态时使用预编译的头。 始终定义此宏。 8. `__cplusplus_cli` 定义为整数文字值 200406 在作为 c + + 编译时和 /clr, ,/clr: pure, ,或 /clr: safe 设置编译器选项。 否则,未定义。 在定义时, `__cplusplus_cli` 有效范围是整个翻译单元。 9. `__cplusplus_winrt` 定义为整数文字值 201009 在作为 c + + 编译时和 /ZW (Windows 运行时编译) 设置编译器选项。否则,未定义。 10. `_CPPRTTI` 定义为 1 如果 /GR (启用运行时类型信息) 设置编译器选项。 否则,未定义。 11. `_CPPUNWIND` 定义为 1,如果一个或多个 /GX (启用异常处理), ,/clr (公共语言运行时编译), ,或 /EH (异常处理模型) 设置编译器选项。 否则,未定义。 12. `_DEBUG` 定义为 1 时 /LDd, ,/MDd, ,或 /MTd 设置编译器选项。 否则,未定义。 13. `_DLL` 定义为 1 时 /MD 或 /MDd 设置 (多线程 DLL) 编译器选项。 否则,未定义。 14. `__FUNCDNAME__` 定义为一个字符串,它包含 修饰名 将封闭函数。 仅在函数内定义宏。 `__FUNCDNAME__` 如果您使用不扩展宏 /EP 或 /P 编译器选项。 15. `__FUNCSIG__` 定义为一个字符串,它包含封闭函数的签名。 仅在函数内定义宏。 `__FUNCSIG__` 如果您使用不扩展宏 /EP 或 /P 编译器选项。 当针对 64 位目标编译,调用约定是 `__cdecl` 默认情况下。 有关用法的示例,请参阅 `__FUNCDNAME__` 宏。 16. `__FUNCTION__` 定义为包含封闭函数的未修饰的名称的字符串文字。 仅在函数内定义宏。 `__FUNCTION__` 如果您使用不扩展宏 /EP 或 /P 编译器选项。 有关用法的示例,请参阅 `__FUNCDNAME__` 宏。 17. `_INTEGRAL_MAX_BITS` 为整数文字值 64 的定义、 非向量整型的最大大小 (以位为单位)。 始终定义此宏。 18. `__INTELLISENSE__` 定义为 1 期间 IntelliSense 编译器将传递在 Visual Studio IDE 中。 否则,未定义。 可以使用此宏来保护的代码 IntelliSense 编译器不了解,或使用它来生成和智能感知编译器之间切换。 有关详细信息,请参阅 的智能感知缓慢疑难解答提示。 19. `_ISO_VOLATILE` 为 1 如果定义 /volatile: iso 设置编译器选项。 否则,未定义。 20. `_KERNEL_MODE` 为 1 如果定义 /kernel (创建内核模式二进制) 设置编译器选项。 否则,未定义。 21. `_M_AMD64` 定义为整数文字值 100 的编译该面向 x64 处理器。 否则,未定义。 22. `_M_ARM` 为整数文字值 7 面向 ARM 处理器的编译进行定义。 否则,未定义。 23. `_M_ARM_ARMV7VE` 定义为 1 时 /arch: armv7ve 面向 ARM 处理器的编译进行设置编译器选项。 否则,未定义。 24. `_M_ARM_FP` 定义为整数文字值,该值指示该 /arch 编译器选项已设置,如果编译目标,则 ARM 处理器。 否则,未定义。 25. `_M_ARM64` 定义为 1 的面向 64 位 ARM 处理器的编译。 否则,未定义。 26. `_M_CEE` 定义为 001 如果任何 /clr (公共语言运行时编译) 设置编译器选项。 否则,未定义。 27. `_M_CEE_PURE` 定义为 001 if /clr: pure 设置编译器选项。 否则,未定义。 28. `_M_CEE_SAFE` 定义为 001 if /clr: safe 设置编译器选项。 否则,未定义。 ## 常用宏 EXE和DLL都定义宏:WIN32,_WINDOWS; Debug定义宏:_DEBUG; Release定义宏:NDEBUG; 多字节字符集定义宏:_MBCS; Unicode定义宏:_UNICODE、UNICODE; _AFXDLL:在共享DLL中使用MFC; _AFXEXT:表示要做一个MFC扩展DLL; _WINDLL:表示要做一个用到MFC的DLL; _USRDLL:表示做一个用户DLL(相对MFC扩展DLL而言)。 以“#”开头的命令都是预处理命令,预处理不是c语言的语句,不能被编译器编译。所以在编译之前需要使用预处理器做文件的预处理工作。在编译之前,所有的预处理语句都要被处理(替换或展开)。