在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
相关推荐
在C语言中,通常通过`stdarg.h`头文件中的`va_list`、`va_start`、`va_arg`和`va_end`宏来实现。这些宏配合`__VA_ARGS__`使用,使得函数能够动态地处理不同数量和类型的参数。 例如,一个简单的可变参数函数可能...
这里,`##__VA_ARGS__`用于将可变参数列表转换为实际参数,从而可以像普通函数调用一样进行传递。 #### 使用注意事项 - **分号吞并问题**:在宏定义中直接包含分号可能会导致意外的副作用。例如,在宏体内直接使用...
### C语言可变参数机制详解:`va_list`与`vsnprintf`及`printf`实现 #### 引言 在C语言编程中,我们经常使用`printf`函数来输出各种格式的数据,但你是否曾思考过,这个看似简单的函数背后隐藏着怎样的复杂性?`...
### C语言可变参数函数实现探究 ...总之,可变参数函数是C语言中一个强大且灵活的功能,通过合理利用`_va_list`和相关宏,可以构建出能够处理动态参数数量的高效函数,这对于编写复杂多变的应用程序具有重要意义。
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__) #define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, "(=_=)", format, ##__VA_ARGS__) #else #...
在C和C++语言中,`test_va_args`是一个常见的示例,用于演示如何在函数中处理可变数量的参数。本文将深入探讨这个问题,讲解如何在数据结构设计中处理参数不确定性。 首先,我们需要理解“可变参数”是什么。在传统...
printf("[DEBUG] " fmt "\n", ##__VA_ARGS__); \ } \ } while (0) ``` 这里的`__VA_ARGS__`是可变参数列表,允许传递任意数量的参数。`DEBUG_PRINT`宏在调试模式下会打印出指定格式的调试信息,而在非调试模式下...
本文介绍了C语言中特殊符号 `#` 和 `##` 的用法以及可变参数宏 `__VA_ARGS__` 的应用。通过具体的示例代码,我们可以看到如何利用这些特性来编写更加灵活和强大的宏。`#` 操作符可以帮助我们在运行时生成动态文本...
这里`__VA_ARGS__`是一个特殊的预处理器标记,它表示传递给宏的额外参数列表。因此,下面的调用: ```c myprintf("Error: %s\n", "an error occurred"); ``` 会被预处理器替换为: ```c fprintf(stderr, "Error: ...
这个头文件中定义了`va_list`、`va_start`、`va_arg`和`va_end`宏,它们是处理可变参数列表的关键。下面逐一解释这些宏的作用: 1. `va_list`: 这是一个类型定义,用来声明一个可变参数列表的变量。例如:`va_list ...
### C语言变参函数设计深度解析 #### 引言 在C语言中,函数的灵活性不仅体现在参数类型上,还体现在参数数量上。通常情况下,函数的参数在定义时就已经固定,但在某些应用场景下,我们需要设计能够接受不确定数量...
在C语言中,`va_arg`是一个非常重要的函数宏,它是可变参数列表(Variable Argument List,也称为 variadic function)处理的关键组成部分。标题“va_arg_c_”可能指的是一个文档,专门探讨了如何在C语言中使用`va_...
C语言可变参数实现机制详解 C语言中的可变参数是一种特殊的函数参数形式,它允许函数接受不定数量和类型的参数。在日常开发中,我们常用的 printf 函数就是使用了可变参数的接口。今天,我们将深入探索 C 语言可变...
#define myprintf(format, ...) fprintf(stderr, format, ##__VA_ARGS__) ``` 这样,即使 `__VA_ARGS__` 为空,宏调用也不会产生编译错误。 #### 总结 本文介绍了C语言中的宏、井号(#)和可变参数的基本概念及...
尽管如此,通过巧妙地利用C语言的宏定义特性,我们可以实现类似泛型的功能,这就是类函数宏技术。 C语言的宏定义是预处理阶段的一部分,预处理程序会在编译之前对源代码进行处理,如替换宏定义、处理条件编译指令等...
这里的`__VA_ARGS__`是C99引入的变长参数宏,它可以捕获并替换后面的所有参数。使用这个宏时,你可以这样写: ```c SINGLE_LINE_COMMENT(这是一条测试注释) ``` 编译后,这将被展开为: ```c // 这是一条测试注释...
在C语言中,可变参数的实现基于调用约定(calling convention),通常涉及到两个关键函数:`va_start`、`va_end` 和一个宏 `va_list`。`va_list` 是一个类型定义,用于存储可变参数列表的指针;`va_start` 用于初始...
它的语法是`va_start(args, last_fixed_arg)`,其中`args`是`va_list`变量,`last_fixed_arg`是函数定义中最后一个固定参数的名称。这样,`va_start`会把`args`指向`last_fixed_arg`后面的第一个可变参数。 3. `va_...