如何制作可变参数宏(可变数量的参数)

has*_*sen 182 c g++ variadic c-preprocessor

我想在C中编写一个宏,它接受任意数量的参数,而不是特定的数字

例:

#define macro( X )  something_complicated( whatever( X ) )
Run Code Online (Sandbox Code Playgroud)

哪里X有任何数量的参数

我需要这个,因为它whatever是重载的,可以用2或4个参数调用.

我试过两次定义宏,但第二个定义覆盖了第一个!

我正在使用的编译器是g ++(更具体地说,mingw)

Ale*_*x B 283

C99方式,也由VC++编译器支持.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

  • `##`不需要,也不便携.`#define FOO(...)printf(__ VA_ARGS __)`以便携方式完成工作; `fmt`参数可以从定义中省略. (100认同)
  • __VA_ARGS__之前##的原因是,如果变量参数列表为空,它会吞下前面的逗号,例如.FOO("a")扩展为printf("a").这是gcc(也许是vc ++)的扩展,C99要求至少有一个参数代替省略号. (91认同)
  • ## - 语法也适用于llvm/clang和Visual Studio编译器.所以它可能不是便携式的,但主要编译器支持它. (9认同)
  • 我不认为C99在__VA_ARGS__之前需要##.那可能只是VC++. (8认同)
  • IIRC,##是GCC特定的,允许传递零参数 (3认同)
  • 如果您想在宏中使用这些参数并且不使用printf或其他采用变量args的函数,该怎么办? (2认同)

cmc*_*abe 34

__VA_ARGS__是标准的方法.如果您不需要,请不要使用特定于编译器的黑客攻击.

我真的很生气,我无法对原帖发表评论.在任何情况下,C++都不是C的超集.使用C++编译器编译C代码真的很愚蠢.不要做Donny不做的事.

  • *"使用C++编译器编译C代码真的很愚蠢"*=>每个人(包括我)都不这么认为.参见例如C++核心指南:**CPL.1:首选C++到C**,[**CPL.2:如果必须使用C,请使用C和C++的公共子集,并将C代码编译为C++**](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rcpl-subset).我捉襟见肘想起了什么,"C-仅-主义"人真正需要,使其价值的兼容子集不是编程,以及C和C++委员会已经在做的是兼容子集可用功. (7认同)
  • 对于真正的跨平台系统项目(例如操作系统),您确实要坚持使用严格的C语言,因为C编译器更为常见。在嵌入式系统中,仍然存在没有C ++编译器的平台。(有些平台只有合格的C编译器!)C ++编译器让我感到紧张,尤其是对于网络物理系统,我想我不是唯一有这种感觉的嵌入式软件/ C程序员。 (3认同)
  • @downbeat 无论您是否使用 C++ 进行生产,如果您担心它的严密性,那么能够使用 C++ 进行编译会为您提供静态分析的魔力。如果您想查询 C 代码库...想知道是否以某种方式使用某些类型,学习如何使用 [type_traits](https://en.cppreference.com/w/cpp/header/ type_traits) 可以为其构建有针对性的工具。您可以通过一些 C++ 知识和您已经拥有的编译器来为 C 的静态分析工具支付大笔费用... (3认同)
  • @HostileFork足够公平,但*当然*C++人员希望鼓励使用C++.但是其他人不同意; 例如,Linux Torvalds显然拒绝了多个提议的Linux内核补丁,这些补丁试图用`klass`替换标识符`class`以允许用C++编译器进行编译.另请注意,有一些差异会让你失望; 例如,三元运算符在两种语言中的评估方式不同,而`inline`关键字意味着完全不同(正如我从另一个问题中学到的). (2认同)

edu*_*ffy 25

我不认为这是可能的,你可以用双parens伪造它...只要你不需要单独的参数.

#define macro(ARGS) some_complicated (whatever ARGS)
// ...
macro((a,b,c))
macro((d,e))
Run Code Online (Sandbox Code Playgroud)

  • 虽然可以使用可变参数宏,但使用双括号是一个很好的建议. (19认同)
  • Microchip 的 XC 编译器不支持可变参数宏,因此这种双括号技巧是您能做的最好的方法。 (2认同)

小智 10

#define DEBUG

#ifdef DEBUG
  #define PRINT print
#else
  #define PRINT(...) ((void)0) //strip out PRINT instructions from code
#endif 

void print(const char *fmt, ...) {

    va_list args;
    va_start(args, fmt);
    vsprintf(str, fmt, args);
        va_end(args);

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

}

int main() {
   PRINT("[%s %d, %d] Hello World", "March", 26, 2009);
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果编译器不了解可变参数宏,您还可以使用以下任一方法去掉PRINT:

#define PRINT //
Run Code Online (Sandbox Code Playgroud)

要么

#define PRINT if(0)print
Run Code Online (Sandbox Code Playgroud)

第一个注释掉PRINT指令,第二个注释因为条件为NULL而阻止PRINT指令.如果设置了优化,编译器应该删除从未执行过的指令,如:if(0)print("hello world"); 或((无效)0);

  • #define PRINT //不会用//替换PRINT (7认同)
  • #define PRINT if(0)print不是一个好主意,因为调用代码可能有自己的else-if调用PRINT.更好的是:#define PRINT if(true);否则打印 (7认同)
  • 标准的"无所事事,优雅地"是做{}而(0) (3认同)

Dar*_*enW 7

在这里解释了 g++,虽然它是 C99 的一部分,所以应该对每个人都适用

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

快速示例:

#define debug(format, args...) fprintf (stderr, format, args)
Run Code Online (Sandbox Code Playgroud)

  • GCC 的可变参数宏不是 C99 可变参数宏。GCC _has_ C99 可变参数宏,但 G++ 不支持它们,因为 C99 不是 C++ 的一部分。 (3认同)
  • 它不是 C99。C99 使用 __VA_ARGS__ 宏)。 (2认同)
  • 这不适用于 printf("hi"); 没有 var args 的地方。有什么通用方法可以解决这个问题吗? (2认同)