`

C语言 ## __VA_ARGS__ 宏

    博客分类:
  • c++
 
阅读更多

在GNU C中,宏可以接受可变数目的参数,就象函数一样,例如: 

1
2
#define pr_debug(fmt,arg...) \
printk(KERN_DEBUG fmt, ##arg)

用可变参数宏(variadic macros)传递可变参数表 
你可能很熟悉在函数中使用可变参数表,如:

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

直到最近,可变参数表还是只能应用在真正的函数中,不能使用在宏中。

C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏。可变参数宏就像下面这个样子:

1
#define debug(...) printf(__VA_ARGS__)

缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__ 把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。例如: 

1
Debug("Y = %d\n", y);


而处理器会把宏的调用替换成: 

1
printf("Y = %d\n", y);


因为debug()是一个可变参数宏,你能在每一次调用中传递不同数目的参数: 

1
debug("test");  // 一个参数


可变参数宏不被ANSI/ISO C++ 所正式支持。因此,你应当检查你的编译器,看它是否支持这项技术。

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

gcc的预处理提供的可变参数宏定义真是好用: 

1
2
3
4
5
6
#ifdef DEBUG
#define dbgprint(format,args...) \
fprintf(stderr, format, ##args)
#else
#define dbgprint(format,args...)
#endif

如此定义之后,代码中就可以用dbgprint了,例如dbgprint("%s", __FILE__);

下面是C99的方法: 

1
#define dgbmsg(fmt,...)     printf(fmt,__VA_ARGS__)

新的C99规范支持了可变参数的宏 
具体使用如下:

以下内容为程序代码:

1
2
3
4
5
6
7
8
#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 标准中,宏可以象函数一样,定义时可以带有可变参数。宏的语法和函数的语法类似。下面有个例子: 

1
#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)


这里,'...'指可变参数。这类宏在被调用时,它(这里指'...')被表示成零个或多个符号,包括里面的逗号,一直到到右括弧结束为止。当被调用时,在宏体(macro body)中,那些符号序列集合将代替里面的__VA_ARGS__标识符。更多的信息可以参考CPP手册。 
GCC始终支持复杂的宏,它使用一种不同的语法从而可以使你可以给可变参数一个名字,如同其它参数一样。例如下面的例子: 

1
#define debug(format, args...) fprintf (stderr, format, args)


这和上面举的那个ISO C定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。 
GNU CPP还有两种更复杂的宏扩展,支持上面两种格式的定义格式。 
在标准C里,你不能省略可变参数,但是你却可以给它传递一个空的参数。例如,下面的宏调用在ISO C里是非法的,因为字符串后面没有逗号: 
debug ("A message") 
GNU CPP在这种情况下可以让你完全的忽略可变参数。在上面的例子中,编译器仍然会有问题(complain),因为宏展开后,里面的字符串后面会有个多余的逗号。

为了解决这个问题,CPP使用一个特殊的'##'操作。书写格式为: 

1
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

这里,如果可变参数被忽略或为空,'##'操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。象其它的pasted macro参数一样,这些参数不是宏的扩展。 
##还可以起到替换作用 
如: 

1
#define FUN(IName)  IName##_ptr


这里将会把IName变成实际数据.

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

1
2
#define DEBUG(args) (printf("DEBUG: "), printf args)
if (n != 0) DEBUG(("n is %d\n", n));


明显的缺陷是调用者必须记住使用一对额外的括弧。 
gcc 有一个扩展可以让函数式的宏接受可变个数的参数。 但这不是标准。另一种 可能的解决方案是根据参数个数使用多个宏 (DEBUG1, DEBUG2, 等等), 或者用逗号玩个这样的花招: 

1
2
3
#define DEBUG(args) (printf("DEBUG: "), printf(args))
#define _ ,
DEBUG("i = %d" _ i);

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

如果你需要替换宏, 使用一个 函数和一个非函数式宏, 如 #define printf myprintf

分享到:
评论

相关推荐

    # ##和 VA ARGS

    在C语言中,通常通过`stdarg.h`头文件中的`va_list`、`va_start`、`va_arg`和`va_end`宏来实现。这些宏配合`__VA_ARGS__`使用,使得函数能够动态地处理不同数量和类型的参数。 例如,一个简单的可变参数函数可能...

    C语言宏定义##连接符和#符的使用

    这里,`##__VA_ARGS__`用于将可变参数列表转换为实际参数,从而可以像普通函数调用一样进行传递。 #### 使用注意事项 - **分号吞并问题**:在宏定义中直接包含分号可能会导致意外的副作用。例如,在宏体内直接使用...

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

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

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

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

    ffmpeg编译的android可用的so文件,多个版本

    #define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(&gt;_&lt;)", format, ##__VA_ARGS__) #define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, "(=_=)", format, ##__VA_ARGS__) #else #...

    test_va_args

    在C和C++语言中,`test_va_args`是一个常见的示例,用于演示如何在函数中处理可变数量的参数。本文将深入探讨这个问题,讲解如何在数据结构设计中处理参数不确定性。 首先,我们需要理解“可变参数”是什么。在传统...

    巧用C语言宏定义实现自动注释调试代码

    printf("[DEBUG] " fmt "\n", ##__VA_ARGS__); \ } \ } while (0) ``` 这里的`__VA_ARGS__`是可变参数列表,允许传递任意数量的参数。`DEBUG_PRINT`宏在调试模式下会打印出指定格式的调试信息,而在非调试模式下...

    特殊符号#,##的用法

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

    c语言宏使用

    这里`__VA_ARGS__`是一个特殊的预处理器标记,它表示传递给宏的额外参数列表。因此,下面的调用: ```c myprintf("Error: %s\n", "an error occurred"); ``` 会被预处理器替换为: ```c fprintf(stderr, "Error: ...

    c语言实现的一个简单的日志函数

    这个头文件中定义了`va_list`、`va_start`、`va_arg`和`va_end`宏,它们是处理可变参数列表的关键。下面逐一解释这些宏的作用: 1. `va_list`: 这是一个类型定义,用来声明一个可变参数列表的变量。例如:`va_list ...

    C语言变参函数设计

    ### C语言变参函数设计深度解析 #### 引言 在C语言中,函数的灵活性不仅体现在参数类型上,还体现在参数数量上。通常情况下,函数的参数在定义时就已经固定,但在某些应用场景下,我们需要设计能够接受不确定数量...

    va_arg_c_

    在C语言中,`va_arg`是一个非常重要的函数宏,它是可变参数列表(Variable Argument List,也称为 variadic function)处理的关键组成部分。标题“va_arg_c_”可能指的是一个文档,专门探讨了如何在C语言中使用`va_...

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

    C语言可变参数实现机制详解 C语言中的可变参数是一种特殊的函数参数形式,它允许函数接受不定数量和类型的参数。在日常开发中,我们常用的 printf 函数就是使用了可变参数的接口。今天,我们将深入探索 C 语言可变...

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

    #define myprintf(format, ...) fprintf(stderr, format, ##__VA_ARGS__) ``` 这样,即使 `__VA_ARGS__` 为空,宏调用也不会产生编译错误。 #### 总结 本文介绍了C语言中的宏、井号(#)和可变参数的基本概念及...

    C语言中基于类函数宏技术的泛型顺序栈的设计与实现.pdf

    尽管如此,通过巧妙地利用C语言的宏定义特性,我们可以实现类似泛型的功能,这就是类函数宏技术。 C语言的宏定义是预处理阶段的一部分,预处理程序会在编译之前对源代码进行处理,如替换宏定义、处理条件编译指令等...

    C语言单行注释的宏

    这里的`__VA_ARGS__`是C99引入的变长参数宏,它可以捕获并替换后面的所有参数。使用这个宏时,你可以这样写: ```c SINGLE_LINE_COMMENT(这是一条测试注释) ``` 编译后,这将被展开为: ```c // 这是一条测试注释...

    C语言中的可变参数

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

    C语言可变参数的使用

    它的语法是`va_start(args, last_fixed_arg)`,其中`args`是`va_list`变量,`last_fixed_arg`是函数定义中最后一个固定参数的名称。这样,`va_start`会把`args`指向`last_fixed_arg`后面的第一个可变参数。 3. `va_...

Global site tag (gtag.js) - Google Analytics