#define 伪指令

预处理器定义伪指令 指示预处理器将宏的所有后续实例替换为指定的替换标记。

#define 伪指令可以包含:
以下是 使用常量的宏与声明的常量之间的一些差异:
  • const 对象受制于变量的作用域限定规则,而使用 #define 创建的常量不受约束。
  • const 对象不同,宏的值不会显示在编译器使用的中间 表示 中,因为它们是内联扩展的。 内联扩展使宏值对调试器不可用。
  • C 仅开始 宏可用于编译时常量表达式,例如位域长度 ,而 const 对象则不能。
  • 仅限 C + + 开始 编译器不对宏(包括宏参数)进行类型检查。

类似对象的宏

类似对象的宏定义 将单个标识替换为指定的替换标记。 例如,以下类似对象的定义会导致预处理器将标识 COUNT 的所有后续实例替换为常量 1000 :
#define COUNT 1000
如果语句
int arry[COUNT];
此宏定义之后,并且在同一编译单元中,预处理器会将语句更改为
int arry[1000];

在预处理器的输出中。

其他定义可以引用标识 COUNT:
#define MAX_COUNT COUNT + 100

预处理器将 MAX_COUNT 的每个后续实例替换为 COUNT + 100,然后预处理器将其替换为 1000 + 100

如果生成了由宏扩展部分构建的数字,那么预处理器不会将结果视为单个值。 例如,以下内容不会导致值 10.2 ,而是导致语法错误。
#define a 10
doubl d = a.2

C++11 开始 在 中, 预处理器中的对象宏诊断被用于为C和C++编译器提供通用的预处理器接口。 C++11 C99 如果对象类宏名称与其在宏定义中的替换列表之间没有空格,那么 C++11 编译器将发出警告消息。 欲了解更多信息,请参阅 C++11 ( C++11 )中采用的 C99 预处理器功能C++11 结束

类似函数的宏

与对象类宏相比,函数类宏定义更复杂,它在括号内声明形参的名称,并以逗号分隔。 一个空的形参列表是合法的: 这样的宏可以用来模拟一个不带自变量的函数。 C99 添加了对具有可变自变量数的类似函数的宏的支持。 XL C++支持具有可变参数数量的函数式宏,作为与C兼容的语言扩展,也是 C++11 的一部分。C++11

类似函数的宏定义:
后跟括号和替换标记中的参数列表的标识。 这些参数嵌入在替换代码中。 空格不能分隔标识 (即宏的名称) 和参数列表的左括号。 必须使用逗号分隔每个参数。

对于可移植性,宏的参数不应超过 31 个。 参数列表可能以省略号 (...) 结尾 作为形参。 在这种情况下,标识 __VA_ARGS__ 可能会出现在替换列表中。

类似于函数的宏调用:
后跟以逗号分隔的自变量列表 (括在括号中) 的标识。 参数数目应与宏定义中的参数数目相匹配,除非定义中的参数列表以省略号结尾。 在后一种情况下,调用中的自变量数目应该 匹配或 超过定义中的参数数目。 多余的称为 尾部参数。 一旦预处理器标识了类似函数的宏调用,就会进行参数替换。 替换代码中的参数将替换为相应的参数。 如果宏定义允许尾部自变量,那么它们将与中间逗号合并以替换标识 __VA_ARGS__,就像它们是单个自变量一样。 在自变量替换其在替换代码中的相应参数之前,将完全替换自变量本身中包含的任何宏调用。
宏自变量可以为空 (由零个预处理令牌组成)。 例如
#define SUM(a,b,c) a + b + c
SUM(1,,3)  /* No error message.
              1 is substituted for a, 3 is substituted for c. */
如果 parameter 列表未以省略号结尾,那么宏调用中的自变量数目必须与相应宏定义中的参数数目相同。 在参数替换期间,所有指定的自变量被替换后剩余的任何自变量 (包括任何分隔逗号) 都将组合成一个称为变量自变量的自变量。 变量自变量将替换替换列表中标识 __VA_ARGS__ 的任何实例。 以下示例对此进行了说明:
#define debug(...)   fprintf(stderr, __VA_ARGS__)

debug("flag");     /*   Becomes fprintf(stderr, "flag");   */
在以下情况下,宏调用自变量列表中的逗号不会充当自变量分隔符:
  • 字符常量
  • 在字符串字面值中
  • 用括号括起
以下行将宏 SUM 定义为具有两个参数 ab 以及替换标记 (a + b):
#define SUM(a,b) (a + b)
此定义将导致预处理器更改以下语句 (如果这些语句出现在先前定义之后):
c = SUM(x,y);
c = d * SUM(x,y);
在预处理器的输出中,这些语句将显示为:
c = (x + y);
c = d * (x + y);
使用括号来确保对替换文本进行正确评估。 例如,定义:
#define SQR(c)  ((c) * (c))
需要定义中每个参数 c 的括号,以便对表达式进行正确求值,如下所示:
y = SQR(a + b);
预处理器将此语句扩展为:
y = ((a + b) * (a + b));
如果定义中没有括号,那么不会保留 预期 求值顺序,并且预处理器输出为:
y = (a + b * a + b);

### 运算符的自变量 替换类似函数的宏中的参数之前进行转换。

定义后,预处理器标识将保持定义 独立于 语言的作用域限定规则。 宏定义的作用域从定义开始,直到迂到相应的 #undef 伪指令时才结束。 如果没有相应的 #undef 伪指令,那么宏定义的作用域将持续到转换单元结束。

递归宏未完全展开。 例如,定义
   #define x(a,b) x(a+1,b+1) + 4
扩展
   x(20,10)
   x(20+1,10+1) + 4

而不是尝试在自身内部扩展宏 x 。 在扩展宏 x 之后,它是对函数 x()的调用。

不需要定义来指定替换标记。 以下定义将从当前文件中的后续行中除去令牌 debug 的所有实例:
#define debug

仅当第二个预处理器 #define 伪指令前面有预处理器 #undef 伪指令时,才能使用第二个预处理器 #define 伪指令来更改定义的标识或宏的定义。 #undef 伪指令会使第一个定义无效,以便可以在重新定义中使用同一标识。

在程序文本中,预处理器不会扫描 宏定义,取消定义宏或宏调用的 注释,字符常量或字符串常量。

以下示例程序包含两个宏定义以及引用了这两个已定义宏的宏调用:

/**This example illustrates #define directives.**/

void printf(const char*, ...);

#define SQR(s)  ((s) * (s))
#define PRNT(a,b) \
  printf("value 1 = %d\n", a); \
  printf("value 2 = %d\n", b)

int main(void)
{
  int x = 2;
  int y = 3;

     PRNT(SQR(x),y);

  return(0);
}

经过 预处理后,此程序将替换为等效于以下内容的代码:

void printf(const char*, ...);

int main(void)
{
  int x = 2;
  int y = 3;

     printf("value 1 = %d\n", ( (x) * (x) ) );
     printf("value 2 = %d\n", y);

  return(0);
}
此程序生成以下输出:
value 1 = 4
value 2 = 3
IBM 扩展
可变宏扩展

变量宏扩展是指与具有可变自变量数的宏相关的 C99 和 C++03 的两个扩展。 一个扩展是用于将变量参数标识从 __VA_ARGS__ 重命名为用户定义的标识的机制。 当未指定任何变量自变量时,另一个扩展提供了一种方法来除去变量宏中的悬空逗号。 这两个扩展都已实现,以便于移植使用 GNU C 和 C++开发的程序。

以下示例演示了如何使用标识来代替 __VA_ARGS__。 宏 debug 的第一个定义说明了 __VA_ARGS__的通常用法。 第二个定义显示使用标识 args 代替 __VA_ARGS__
#define debug1(format, ...)  printf(format, ## __VA_ARGS__)
#define debug2(format, args ...)  printf(format, ## args)
调用 宏扩展的结果
debug1("Hello %s/n", "World"); printf("Hello %s/n", "World");
debug2("Hello %s/n", "World"); printf("Hello %s/n", "World");
如果函数宏的变量自变量被省略或为空,并且后跟 ## 的逗号在函数宏定义中的变量自变量标识之前,那么预处理器将除去尾部逗号。
IBM 扩展

C++11 在 中,变量宏功能和关于空宏参数的更改是从 预处理器中采用的,为C和C++编译器提供了一个通用的预处理器接口。 C++11 C99 C++11中支持变量宏和空宏参数。 有关更多信息,请参阅 C++11 中采用的 C99 预处理器功能部件 (C++11)