`

可变参数宏 __VA_ARGS__

    博客分类:
  • c++
 
阅读更多
转自: http://blog.sina.com.cn/s/blog_661314940100qmfg.html
在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:
#define pr_debug(fmt,arg...) \
printk(KERN_DEBUG fmt,##arg)



用可变参数宏(variadic macros)传递可变参数表
你可能很熟悉在函数中使用可变参数表,如:
void printf(const char* format, …);
直到最近,可变参数表还是只能应用在真正的函数中,不能使用在宏中。
C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏。可变参数宏就像下面这个样子:
#define debug(…) printf(__VA_ARGS__)
缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__ 把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。例如:
Debug(“Y = %d\n”, y);
而处理器会把宏的调用替换成:
printf(“Y = %d\n”, y);
因为debug()是一个可变参数宏,你能在每一次调用中传递不同数目的参数:
debug(“test”); //一个参数
可变参数宏不被ANSI/ISO C++ 所正式支持。因此,你应当检查你的编译器,看它是否支持这项技术。

用GCC和C99的可变参数宏, 更方便地打印调试信息

gcc的预处理提供的可变参数宏定义真是好用:
#ifdef DEBUG
#define dbgprint(format,args...) \
fprintf(stderr, format, ##args)
#else
#define dbgprint(format,args...)
#endif
如此定义之后,代码中就可以用dbgprint了,例如dbgprint("aaa %s", __FILE__);。感觉这个功能比较Cool  :em11:

下面是C99的方法:
#define dgbmsg(fmt,...) \
             printf(fmt,__VA_ARGS__)


新的C99规范支持了可变参数的宏
具体使用如下:
以下内容为程序代码:
#include <stdarg.h> #include <stdio.h>
#define LOGSTRINGS(fm, ...) printf(fm,__VA_ARGS__)
int main() {      LOGSTRINGS("hello, %d ", 10);      return 0; }
但现在似乎只有gcc才支持。

可变参数的宏里的‘##’操作说明

带有可变参数的宏(Macros with a Variable Number of Arguments)
在1999年版本的ISO C 标准中,宏可以象函数一样,定义时可以带有可变参数。宏的语法和函数的语法类似。下面有个例子:
#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)
这里,‘…’指可变参数。这类宏在被调用时,它(这里指‘…’)被表示成零个或多个符号,包括里面的逗号,一直到到右括弧结束为止。当被调用时,在宏体(macro body)中,那些符号序列集合将代替里面的__VA_ARGS__标识符。更多的信息可以参考CPP手册。
GCC始终支持复杂的宏,它使用一种不同的语法从而可以使你可以给可变参数一个名字,如同其它参数一样。例如下面的例子:
#define debug(format, args...) fprintf (stderr, format, args)
这和上面举的那个ISO C定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。
GNU CPP还有两种更复杂的宏扩展,支持上面两种格式的定义格式。
在标准C里,你不能省略可变参数,但是你却可以给它传递一个空的参数。例如,下面的宏调用在ISO C里是非法的,因为字符串后面没有逗号:
debug ("A message")
GNU CPP在这种情况下可以让你完全的忽略可变参数。在上面的例子中,编译器仍然会有问题(complain),因为宏展开后,里面的字符串后面会有个多余的逗号。
为了解决这个问题,CPP使用一个特殊的‘##’操作。书写格式为:
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
这里,如果可变参数被忽略或为空,‘##’操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。象其它的pasted macro参数一样,这些参数不是宏的扩展。





怎样写参数个数可变的宏

一种流行的技巧是用一个单独的用括弧括起来的的 ``参数" 定义和调用宏, 参数在 宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。
#define DEBUG(args) (printf("DEBUG: "), printf args)

if(n != 0) DEBUG(("n is %d\n", n));
明显的缺陷是调用者必须记住使用一对额外的括弧。
gcc 有一个扩展可以让函数式的宏接受可变个数的参数。 但这不是标准。另一种 可能的解决方案是根据参数个数使用多个宏 (DEBUG1, DEBUG2, 等等), 或者用 逗号玩个这样的花招:
#define DEBUG(args) (printf("DEBUG: "), printf(args))
#define _ ,

DEBUG("i = %d" _ i);
C99 引入了对参数个数可变的函数式宏的正式支持。在宏 ``原型" 的末尾加上符号 ... (就像在参数可变的函数定义中), 宏定义中的伪宏 __VA_ARGS__ 就会在调用是 替换成可变参数。
最后, 你总是可以使用真实的函数, 接受明确定义的可变参数

如果你需要替换宏, 使用一个 函数和一个非函数式宏, 如 #define printf myprintf。
分享到:
评论

相关推荐

    __VA_ARGS__用法

    `__VA_ARGS__` 是一个宏,它代表可变参数列表中的实际参数。 - `__FILE__`: 这个宏在预编译时会被替换成当前源文件的完整路径。 - `__LINE__`: 这个宏在预编译时会被替换成当前行号。 - `__FUNCTION__`: 这个宏在预...

    va_args_count:使用AC宏在C99 __VA_ARGS__宏中查找参数的数量

    当在其他可变参数宏中使用VA_ARGS_COUNT()宏时,它确实发光。 您可能有一个函数,需要使用可变数量的参数,如下所示: void print_strings(int count, const char *s1, ...); 其中第一个参数count指定传递给该...

    # ##和 VA ARGS

    在C/C++编程语言中,`#`、`##` 和 `__VA_ARGS__` 是预处理器宏中的特殊符号,它们在处理可变参数宏时起着关键作用。本文将详细探讨这三个符号以及它们在可变参数函数中的应用。 ### 1. `#` 符号 `#` 符号在宏定义...

    特殊符号#,##的用法

    本文介绍了C语言中特殊符号 `#` 和 `##` 的用法以及可变参数宏 `__VA_ARGS__` 的应用。通过具体的示例代码,我们可以看到如何利用这些特性来编写更加灵活和强大的宏。`#` 操作符可以帮助我们在运行时生成动态文本...

    详解_C语言可变参数_va_list和_vsnprintf及printf实现

    ### C语言可变参数机制详解:`va_list`与`vsnprintf`及`printf`实现 #### 引言 在C语言编程中,我们经常使用`printf`函数来输出各种格式的数据,但你是否曾思考过,这个看似简单的函数背后隐藏着怎样的复杂性?`...

    C、C++中变参数宏

    今天,我们将讨论 C、C++ 中的变参数宏,包括可变参数宏和 _VA_ARGS_ 的使用。 一、可变参数宏 可变参数宏是 C 语言中的一种特殊宏定义,允许宏定义中包含可变数量的参数。这种宏定义使用省略号(...)来表示可变...

    c语言中可变参数va宏的使用

    可变参数宏在C语言中提供了一种灵活的方式来处理不定数量和类型的参数。通过对`va_list`、`va_start`、`va_arg`和`va_end`的理解与运用,开发者可以在嵌入式系统以及其他应用场景中更好地管理动态数据。这些宏不仅...

    C语言可变参数_va_list和_vsnprintf及printf实现.

    ### C语言可变参数函数实现探究 ...总之,可变参数函数是C语言中一个强大且灵活的功能,通过合理利用`_va_list`和相关宏,可以构建出能够处理动态参数数量的高效函数,这对于编写复杂多变的应用程序具有重要意义。

    test_va_args

    `va_start`、`va_arg`和`va_end`是三个关键宏,它们分别用于初始化可变参数列表、获取下一个参数和清理资源。 1. **`va_start`宏**:用于初始化`va_list`。它的语法是`va_start(list, lastFixedArg)`,其中`list`是...

    cpp代码-C和C++奇怪内容 ## # __VA_ARGS__和... (可变参数) 宽字符

    `__VA_ARGS__` 是可变参数宏的一个特殊占位符,用于访问宏定义中带有不定数量参数的列表。它通常与 `...` 结合使用。例如,你可以定义一个打印任意数量参数的宏: ```cpp #define PRINT(...) printf(__VA_ARGS__)...

    参数-VA-ARGS- 与...的用法与区别

    首先,`__VA_ARGS__` 是预处理器宏中的一个特殊符号,主要用于宏定义中的可变参数。当定义一个宏,如: ```cpp #define PRINTF_FORMATTED(...) printf(__VA_ARGS__) ``` 在这个例子中,`__VA_ARGS__` 会捕获宏调用...

    详解-C语言可变参数-va-list和-vsnprintf及printf实现.pdf

    C语言可变参数实现机制详解 ...我们可以通过 va_list 和 vsnprintf 等机制来实现可变参数的函数,并且可以通过栈操作来确定可变参数的位置。但是,这只是一个简单的示例代码,实际上还需要根据具体情况进行调整和优化。

    c 语言中的宏,井号,可变参数.doc

    在这个例子中,`__VA_ARGS__` 是一个特殊标识符,用于代表传递给宏的可变参数列表。例如: ```c myprintf("Error: %s\n", "Something went wrong"); ``` 上述调用等价于: ```c fprintf(stderr, "Error: %s\n", ...

    C语言中的可变参数

    在C语言中,可变参数的实现基于调用约定(calling convention),通常涉及到两个关键函数:`va_start`、`va_end` 和一个宏 `va_list`。`va_list` 是一个类型定义,用于存储可变参数列表的指针;`va_start` 用于初始...

    关于可变参数va系列函数用法的几个例子

    `va_start`是用于初始化可变参数列表的宏。它的语法如下: ```cpp va_start(ap, last_fixed_arg); ``` 这里的`ap`是一个指向可变参数列表的指针(通常是一个`va_list`类型的变量),`last_fixed_arg`是可变参数...

    C语言可变参数的使用

    2. `va_start`:这个宏用于初始化`va_list`,告诉编译器从哪个参数开始处理可变参数。它的语法是`va_start(args, last_fixed_arg)`,其中`args`是`va_list`变量,`last_fixed_arg`是函数定义中最后一个固定参数的...

    C语言可变参数及printf函数的实现

    这些宏提供了访问和遍历可变参数列表的方法。`va_start`初始化`va_list`,`va_arg`用于获取下一个参数,而`va_end`在使用完所有参数后结束访问。 #### 结论 通过以上分析,我们可以看到,尽管可变参数函数在编写时...

    C/C++宏定义的可变参数详细解析

    总结来说,C/C++中的可变参数宏是通过`...`和`__VA_ARGS__`来实现的,`##`运算符则用于处理空参数时的逗号问题。GCC提供了额外的扩展,使得宏的使用更加灵活,但同时也需要开发者更加注意可能的陷阱。在实际编程中,...

    关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)

    `va_start`宏用于初始化`va_list`指针,使其指向可变参数列表的开始。它的两个参数分别是`va_list`指针和最后一个固定参数。例如,`va_start(arg_ptr, i)`,其中`i`是`simple_va_fun`函数中最后一个已知参数,`arg_...

Global site tag (gtag.js) - Google Analytics