C语言中的可变宏

宏可以被声明为接受可变数量的参数,就像函数一样。定义宏的语法与函数的语法相似。以下是一个示例:

#define eprintf(...) fprintf (stderr, __VA_ARGS__)

这种宏被称为可变宏。调用宏时,其参数列表中最后一个命名参数(此宏没有)之后的所有token,包括任何逗号,都将成为可变参数。此token序列将替换宏体中出现的标识符__VA_ARGS__。因此,我们有这样的展开:

eprintf ("%s:%d: ", input_file, lineno)fprintf (stderr, "%s:%d: ", input_file, lineno)

可变参数在插入到宏展开之前是被完全宏展开的,就像普通参数一样。您可以使用 '#''##' 操作符对可变参数进行字符串化,或将其前导或尾部的token与另一个token拼接在一起。(但'##'有个重要的特殊案例,请参见下文。)

如果宏很复杂,则可能需要一个比__VA_ARGS__更具描述性的可变参数名称。CPP允许这样做,作为宏展开。您可以在'…'之前写一个参数名称;该名称用于可变参数。上面的eprintf宏可以这样写:

#define eprintf(args…) fprintf (stderr, args)

可以使用这样的展开。不能在同一宏中既使用__VA_ARGS__又使用此展开。

在可变宏中既可以有命名参数又可以有可变参数。我们可以这样定义eprintf,所以:

#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

这个公式看起来更具描述性,但从以往历史来看,它不那么灵活:你必须在格式字符串之后提供至少一个参数。在标准C中,你不能省略将命名参数与可变参数分隔开的那个逗号。(请注意,此限制已在C++20中取消,且在GNU C中从未存在过;请参阅下文。)

此外,如果可变参数为空,则会出现语法错误,因为格式字符串后面会有一个额外的逗号。

eprintf("success!\n", );fprintf(stderr, "success!\n", );

这已经在C++20中得到了修复,GNU CPP也有一对展开来处理这个问题。

首先,在GNU CPP中,以及从C++20开始的C++中,允许完全省略可变参数:

eprintf ("success!\n")fprintf(stderr, "success!\n", );

其次,C++20引入了__VA_OPT__函数宏。此宏可能只会出现在可变宏的定义中。如果可变参数有任何token,那么对__VA_OPT__的调用将展开到其参数;但如果可变参数没有任何token,__VA_OPT__将展开为空:

#define eprintf(format, ...) \
  fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)

__VA_OPT__ 在 GNU C 和 GNU C++中也是有效的。

从以往历史来看,GNU CPP也有另一种展开来处理尾部逗号:当'##'token被放在逗号和可变参数之间时,它拼接操作数会具有特殊含义。尽管引入了__VA_OPT__,但为了向后兼容,GNU CPP仍然支持此展开。如果你这样写

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

并且在调用eprintf宏时将可变参数省去,那么'##'之前的逗号将被删除。但是要注意,如果你传递了一个空参数,则这种情况不会发生;另外,如果'##'前面的token不是逗号,那么也不会出现这种情况。

eprintf ("success!\n")fprintf(stderr, "success!\n");

对于唯一的宏参数就是可变参数的情况,上面的解释是模棱两可的,因为该宏在使用时,如果什么参数也没有,那么试图区分是空参数还是缺少参数是没有意义的。CPP在符合特定的C标准时会保留逗号。否则,在标准规定下,逗号将在宏展开时被删除。

C标准强制性规定,标识符__VA_ARGS__只能出现在可变宏的替换列表中。它不能被用作宏名称、宏参数名称,或用于其他类型的宏中。在开放文本中也可能被禁止使用;标准的这个规定含糊不清。我们建议您避免使用它,除非是用于它的本意目的。

同样,C++禁止__VA_OPT__出现在可变宏的替换列表之外的任何位置。

随着C99的出现,可变宏成为C语言的标准组成部分。GNU CPP以前使用命名变量参数('args…',而不是'…'__VA_args__)来支持它们,这一支持仍然有效,且向后兼容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值