预处理器定义宏

本文详细介绍了C/C++预处理器定义宏的相关知识。包括宏的概念、类型,宏在C++中的应用及与内联函数的对比,还列举了标准和Microsoft专用的预定义宏,以及常用宏的示例,同时说明了带参数宏和函数的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

预处理器定义宏

宏(C/C++)

预处理在非预处理指令的所有行(即第一个非空白字符不是 # 的行)中展开宏,并在指令的某些部分(没有作为条件编译的一部分跳过)中展开宏。利用“条件编译”指令,您可以通过测试一个常量表达式或标识符取消对源文件的某些部分的编译,以确定将哪些文本块传递给编译器以及在预处理期间将从源文件中删除哪些文本块。

#define指令通常用于将有用标识符与常量、关键字和常用语句或表达式关联。表示常量的标识符有时称为“符号常量”或“清单常量”。表示语句或表达式的标识符称为“宏”。在该预处理器文档中,仅使用术语“宏”。

在程序源文本或某些其他预处理器命令的参数中识别宏的名称时,它将被视为对该宏的调用。宏名称将替换为宏主体的副本。如果宏接受参数,则宏名称后面的实参将替换为宏主体内的形参。将宏调用替换为已处理的主体副本的过程称为宏调用的“扩展”。

实际上,有两种类型的宏。“类似于对象的”宏不采用参数,但可以定义“类似于函数的”宏以接受参数,以便其外观和行为类似于函数调用。由于宏无法生成实际函数调用,因此您有时可以将函数调用替换为宏以使程序更快地运行(在C++中,内联函数通常是首选方法)。但是,如果未定义和小心使用宏,则宏会导致出现问题。必须在带有参数的宏定义中使用括号,以便在表达式中保持适当的优先级。此外,宏无法正确处理具有副作用的表达式。有关详细信息,请参阅#define指令中的getrandom示例。

一旦定义宏,则无法在未先删除原始定义的情况下将其重定义为不同的值。但是,您可以使用完全相同的定义来重定义宏。因此,相同的定义可在一个程序中出现多次。

#undef指令将删除宏的定义。一旦删除定义,就可以将该宏重定义为不同的值。#define指令和#undef指令分别讨论了#define#undef指令。

宏和C++

C++提供新功能,其中一些取代了ANSI C预处理器提供的功能。这些新功能可增强语言的类型安全性和可预见性:

  1. C++中,声明为const的对象可用于常量表达式。这使程序可以声明具有类型和值信息的常量,以及可以用调试器以符号方式查看的枚举。使用预处理器#define指令定义常量不是那么精确。除非在程序中找到采用const对象的地址的表达式,否则不会为该对象分配存储区。
  2. C++内联函数功能取代了函数类型宏。使用内联函数取代宏的好处如下:
    类型安全。内联函数需要接受与常规函数相同的类型检查。宏不是类型安全。
    纠正具有副作用的参数的处理。内联函数将计算在输入函数体前作为参数提供的表达式。因此,具有副作用的表达式不可能不安全。

有关内联函数的详细信息,请参阅 inline__inline __forceinline

出于向后兼容性的原因,将为Microsoft C++保留存在于ANSI C以及更早的C++规范中的所有预处理器工具。

预定义的宏

Visual C+ +编译器预定义某些预处理器宏,具体取决于语言(CC + +)、编译目标,以及选择的编译器选项。

Visual c + + 支持 ANSI/ISO C99 标准和 ISO C + + 14 标准所指定的所需预定义的预处理器宏。该实现还支持几个更多特定于 Microsoft 的预处理器宏。仅针对特定的生成环境或编译器选项定义一些宏。除非另有说明,宏的定义整个翻译单元如同它们指定为 /D 编译器选项参数。在定义时,宏是由预处理器在编译前扩展为指定的值。预定义的宏不采用任何参数,并且不能重新定义。

标准的预定义的标识符

编译器支持 ISO C99 ISO C + + 11 中指定此预定义的标识符。

  1. __func__为函数本地将封闭函数的未限定和未修饰名称 static const 数组 char

例程:

void example(){ 

    printf("%s\n", __func__); 

} // prints "example" 

标准预定义的宏

编译器支持 ISO C99 ISO C + + 14 标准所指定的这些预定义的宏。

  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 专用预定的义宏

Microsoft Visual c + + 支持这些其他预定义的宏。

  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 设置编译器选项。 否则,未定义。
  29. _M_FP_EXCEPT 定义为 1 如果 /fp /fp: strict 设置编译器选项。 否则,未定义。
  30. _M_FP_FAST 定义为 1 如果 /fp:fast 设置编译器选项。 否则,未定义。
  31. _M_FP_PRECISE 定义为 1 如果 /fp 精确 设置编译器选项。 否则,未定义。
  32. _M_FP_STRICT 定义为 1 如果 /fp: strict 设置编译器选项。 否则,未定义。
  33. _M_IX86 定义为整数文字值 600 的编译该面向 x86 处理器。 没有为 x64 ARM 编译目标定义此宏。
  34. _M_IX86_FP 定义为整数文字值,该值指示 /arch 编译器选项的设置,则默认值。 此宏始终定义的编译目标时 x86 处理器。 否则,未定义。
  35. _M_X64 定义为整数文字值 100 的编译该面向 x64 处理器。 否则,未定义。
  36. _MANAGED 1 /clr 设置编译器选项。 否则,未定义。
  37. _MSC_BUILD 定义为整数文字,其中包含编译器的版本号的修订号元素。 修订版本号是版本号的句点分隔的第四个元素。 例如,如果 Visual c + + 编译器的版本号为 15.00.20706.01 _MSC_BUILD 宏计算结果为 1 始终定义此宏。
  38. _MSC_EXTENSIONS 定义为 1 如果 /Ze (启用语言扩展) 设置编译器选项,这是默认设置。 否则,未定义。
  39. _MSC_FULL_VER 定义为整数文本进行编码主要,次版本号和内部数字元素的数目的编译器的版本号。 主版本号是句点分隔的版本号的第一个元素、 次版本号是第二个元素和内部版本号是第三个元素。 例如,如果 Visual c + + 编译器的版本号为 15.00.20706.01 _MSC_FULL_VER 宏计算结果为 150020706 输入 cl / 通过在命令行查看编译器的版本号。 始终定义此宏。
  40. _MSC_VER 作为整形文本进行编码编译器的版本号的主要和次要数字元素定义。 主版本号是句点分隔的版本号的第一个元素和次版本号是第二个元素。 例如,如果 Visual c + + 编译器的版本号为 17.00.51106.1 _MSC_VER 宏计算结果为 1700年。 输入 cl / 通过在命令行查看编译器的版本号。 始终定义此宏。
  41. _MSVC_LANG 定义为一个整数,指定由编译器针对 c + + 语言标准。 如果宏时编译为 c + +,为整数型值 201402 /std:c + + 14 编译器选项是否设置,默认情况下,它是设置为更高时,未指定时,值 /std:c + + 中最新 设置编译器选项。 否则,该宏是不确定的。 _MSVC_LANG 宏和 /std (指定语言标准版本) 编译器选项是在 Visual Studio 2015 更新 3 开始。
  42. __MSVC_RUNTIME_CHECKS 定义为 1,如果其中的 /RTC 设置编译器选项。 否则,未定义。
  43. _MT 定义为 1 /MD /MDd (多线程 DLL) /MT /MTd 指定 (多线程)。 否则,未定义。
  44. _NATIVE_WCHAR_T_DEFINED 定义为 1 /zc: wchar_t 设置编译器选项。 否则,未定义。
  45. _OPENMP 定义为整数文字 200203 表示由 Visual c + + 中实现的 OpenMP 规范的日期,如果 /openmp (启用 OpenMP 2.0 支持) 设置编译器选项。 否则,未定义。
  46. _PREFAST_ 定义为 1 / 分析 设置编译器选项。 否则,未定义。
  47. __TIMESTAMP__ 作为一个字符串,它包含的日期和时间的当前源文件返回的 C 运行库的缩写,常量长度窗体的上次修改定义 asctime 正常工作,例如, Fri 19 Aug 13:32:58 2016 始终定义此宏。
  48. _VC_NODEFAULTLIB 定义为 1 /Zl (省略默认库名) 设置编译器选项。 否则,未定义。
  49. _WCHAR_T_DEFINED 定义为 1 时默认 /zc: wchar_t 设置编译器选项。 _WCHAR_T_DEFINED 宏定义,但如果不具有任何值 /Zc:wchar_t- 设置编译器选项,并 wchar_t 项目中包含的系统标头文件中定义。 否则,未定义。
  50. _WIN32 定义为 1 时编译目标是 32 ARM x86,在 64 ARM x64 否则,未定义。
  51. _WIN64 编译目标为 64 ARM x64 时定义为 1 否则,未定义。
  52. _WINRT_DLL 定义为 1 时编译为 c + + /ZW Windows 运行时编译) /LD /LDd 设置编译器选项。 否则,未定义。

用于确定 ATL MFC 库版本的预处理器宏不是由编译器预定义的。 因此它们未定义的预处理器指令中包含必需的标头之前,这些宏的定义在库的标头。

  1. _ATL_VER < atldef.h > 中定义为整数文本进行编码的 ATL 版本编号。
  2. _MFC_VER < afxver_.h > 中定义为整数文本进行编码的 MFC 版本编号。

常用宏

EXEDLL都定义宏:WIN32_WINDOWS

Debug定义宏:_DEBUG

Release定义宏:NDEBUG

多字节字符集定义宏:_MBCS

Unicode定义宏:_UNICODEUNICODE

_AFXDLL:在共享DLL中使用MFC

_AFXEXT:表示要做一个MFC扩展DLL

_WINDLL:表示要做一个用到MFCDLL

_USRDLL:表示做一个用户DLL(相对MFC扩展DLL而言)。

以“#”开头的命令都是预处理命令,预处理不是c语言的语句,不能被编译器编译。所以在编译之前需要使用预处理器做文件的预处理工作。在编译之前,所有的预处理语句都要被处理(替换或展开)。

1:一般宏定义

#define NUM 100

  1. 宏替换:将NUM替换为100。双引号的内容不会被替换;
  2. 宏不会分配内存空间,没有数据类型;
  3. 宏定义末尾不能有分号;
  4. 宏定义一般要大写;
  5. 宏定义可以定义已经定义的宏。

2:有参数的宏定义

#define S(a,b) a*b

  1. S(a,b) 替换为 a*b 表达式;
  2. 注意优先级:a = 4+5b = 10,则替换后为4+5*10,优先算乘法;
  3. 解决优先级问题的方法:加括号#define  S(a,b)  (a)*(b)

3:写一个宏,返回较大的一个

#define MAX(a,b) a>b?a:b

  1. 直接使用MAX(a,b),返回较大的一个数。

4:求一年中有多少秒(忽略闰年)

#define SECOND_YEAR (365*24*60*60)UL

  1. 无需自己计算具体秒数,预处理会计算。
  2. UL作用:表示无符号长整型。这个表达式会使16位的整型数溢出,用到长整型符号L,告诉编译器这个数是长整型数;

【注】带参数的宏和函数的区别

宏展开会使源程序变长,而函数调用不会;

宏替换不会占用运行时间,只占编译时间,而函数调用占用运行时间(分配单元,保留现场,传递值,返回)

符合ANSI的预定义宏

  1. __DATE__:表示当前源文件编译时的日期,格式为:月//年(Mmm dd yyyy)。
  2. __FILE__:表示当前正在处理的源文件名称。
  3. __LINE__:表示当前正在处理的源文件的行,可以用#line指令修改。
  4. __STDC__:表示是ANSI C标准。只有在编译器选项指定了/Za,并且不是编译C++程序时,被定义为常整数1;否则未定义。
  5. __TIME__:表示当前源文件的最近编译时的时间,格式为:小时//秒(hh:mm:ss)。
  6. __TIMESTAMP__:表示当前源文件的最近修改日期和时间,格式为:Ddd Mmm dd hh:mm:ss yyyy,其中Ddd是星期的缩写。

Microsoft相关的宏

  1. _ATL_VER:定义了ATL的版本。
  2. _CHAR_UNSIGNED:设置默认的char类型是unsigned的。只有在编译器选项/J指定时才有定义。
  3. __CLR_VER:指定了应用程序编译时的通用语言运行时(CLR)的版本。格式为:Mmmbbbbb,其中MCLR的主版本,mmCLR的次版本,bbbbbbuild号。
  4. __cplusplus_cli:只有在用/clr/clr:pure/clr:safe编译时才有定义。__cplusplus_cli的值是200406
  5. __COUNTER__:为一个整数,从0开始,每出现一次,其值增加1。可以使用__COUNTER__作为前缀来产生唯一的名字。
  6. __cplusplus只有在编译C++程序时才有定义,一般用于区分C程序和C++程序。
  7. _CPPLIB_VER:在程序中如果包含了任意C++标准库头文件,则_CPPLIB_VER有定义。用于显示正在使用的头文件的版本。
  8. _CPPRTTI:用于标识编译器是否指定了RTTI。如果编译器选项中设定了/GR(打开运行时类型信息机制-RTTI),则_CPPRTTI有定义。
  9. _CPPUNWIND:用于标识编译器是否打开异常处理。如果编译器选项中设定了/GX,则_CPPRTTI有定义。
  10. _DEBUG:用于标识是Debug模式。在编译器指定了/LDd/MDd/MTd时才有定义。
  11. _DLL:当编译器选项指定了/MD/MDdMultithread DLL)时才有定义。
  12. __FUNCDNAME__:只有在函数内部才有效。返回该函数经编译器修饰后的名字。如果编译器选项中设定了/EP/P,则__FUNCDNAME__是未定义。
  13. __FUNCSIG__:只有在函数内部才有效,并且返回该函数的签名。一个函数的签名由函数名、参数列表、返回类型、内含的命名空间组成。如果它是一个成员函数,它的类名和const/volatile限定符也将是签名的一部分。在64位操作系统中,__cdecl是默认的函数调用方式。如果编译器选项中设定了/EP/P,则__FUNCSIG__是未定义。
  14. __FUNCTION__:只有在函数内部才有效。返回该函数未经修饰的名字。如果编译器选项中设定了/EP/P,则__FUNCTION__是未定义。
  15. _INTEGRAL_MAX_BITS:表示整数类型的最大位数(bits)。
  16. _M_ALPHA:为DEC ALPHA平台定义。(现在已不支持)
  17. _M_CEE:当使用/clr的任意形式(/clr:oldSyntax, 例如/clr:safe)编译时被定义。
  18. _M_CEE_PURE:当使用/clr:pure编译时被定义。
  19. _M_CEE_SAFE:当使用/clr:safe编译时被定义。
  20. _M_IX86:为x86处理器架构定义。当值为300时说明是80386,值是400时说明是80486
  21. _M_IA64:为Itanium处理器家族的64位处理器(IA64)架构定义。
  22. _M_IX86_FP:表示编译器选项/arch的值。0/arch未指定;1:指定/arch:SSE2:指定/arch:SSE2
  23. _M_MPPC:为Power Macintosh平台定义。(现在已不支持)
  24. _M_MRX000:为MIPS平台定义。(现在已不支持)
  25. _M_PPC:为PowerPC平台定义。(现在已不支持)
  26. _M_X64:为x64处理器架构定义。 
  27. _MANAGED:当编译器选项指定/clr时定义,其值为1
  28. _MFC_VER:指定MFC版本。例如:0x0700表示MFC version 7
  29. _MSC_BUILD:表示编译器版本号的修订号部分。修订号是以时期进行分割的版本号的第四部分。例如:如果VC++编译器的版本号是15.00.20706.01,则_MSC_BUILD的值为1
  30. _MSC_EXTENSIONS:当指定编译器选项/Ze(默认)时有定义,其值为1
  31. _MSC_FULL_VER:表示编译器的主,次版本号及build号。主版本号是整个版本号的第一部分,次版本号是整个版本号的第二部分,build号是整个版本号的第三部分。例如: 如果VC++编译器的版本号是15.00.20706.01,则_MSC_FULL_VER的值为150020706。可以在命令行键入cl /?来查看编译器的版本号。
  32. _MSC_VER:表示编译器的主,次版本号。例如: 如果VC++编译器的版本号是15.00.20706.01,则_MSC_VER的值为1500
  33. __MSVC_RUNTIME_CHECKS:当指定编译器选项/RTC之一(/RTCs/RTCu/RTC1)时有定义。
  34. _MT:当指定编译器选项/MD/MDd(Multithreaded DLL)/MT/MTd(Multithreaded)时有定义。
  35. _NATIVE_WCHAR_T_DEFINED:当指定编译器选项/Zc:wchar_t(将wchar_t视为内置类型)时有定义。
  36. _OPENMP:当指定编译器选项/openmp时有定义,返回一个表示Visual C++中的OpenMP的日期的整数。
  37. _VC_NODEFAULTLIB:当指定编译器选项/Zl(忽略默认库名)时有定义。
  38. _WCHAR_T_DEFINED:当指定编译器选项/Zc:wchar_t或工程中包含的系统头文件中定义了wchar_t时有定义。
  39. _WIN32:为Win32Win64应用程序定义。总有定义。
  40. _WIN64:为Win64应用程序定义。
  41. _Wp64:当指定编译器选项/Wp64时有定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值