C语言函数参数 args,C语言--#、##、__VA_ARGS__ 和##__VA_ARGS__ 的使用

#  用来把参数转换成字符

#include

#define FUN(X) (printf("%s=%d\n",#X,X)) /* #用来把参数转换成字符 */

int test(int argc, char ** argv)

{

int a = 1;

int b = 2;

FUN(a);

FUN(b);

FUN(a+b);

return 0;

}

/** 程序输出结果:

*******************************

a=1

b=2

a+b=3

*******************************

*/

2、## 把两个语言符号组合成单个语言符号

#include

#define XNAME(n) x##n /* ## 这个运算符把两个语言符号组合成单个语言符号*/

#define PXN(n) printf("x"#n" = %d\n",x##n)

int main(int argc, char ** argv)

{

int XNAME(1) = 10886; /*宏展开就是:x1 = 10086*/

PXN(1); /*宏展开就是:printf("x1 = %d\n",x1) */

return 0;

}

/** 程序输出结果:

*******************************

x1 = 10886

*******************************

*/

3、__VA_ARGS__ 和 ##__VA_ARGS__

#include "stdio.h"

#define DEBUG1(format, ...) do{ \

printf(format, __VA_ARGS__); \

\

} while(0)

#define DEBUG2(format, args...) do{ \

printf(format, ##args); \

\

} while(0)

#define DEBUG3(format, ...) do{ \

printf(format, ##__VA_ARGS__); \

\

} while(0)

int

main(int argc, char **argv)

{

printf("hello world.1 \n");

//DEBUG1("hello world.2\n");//错误 参数为零

DEBUG1("hello world.2 %d %d\n", 1, 2);

DEBUG2("hello world.3\n");

DEBUG2("hello world.3 %d %d %d\n", 1, 2, 3);

DEBUG3("hello world.4\n");

DEBUG3("hello world.4 %d %d %d %d\n", 1, 2, 3, 4);

return 0;

}

应用:

#include "stdio.h"

#define DEBUG_ON

#ifdef DEBUG_ON

#define DEBUG(format, ...) do{ \

printf("File:%s, Line:%d, "format"", __FILE__, __LINE__, ##__VA_ARGS__); \

\

} while(0)

#else

#define DEBUG(format, ...)

#endif

int

main(int argc, char **argv)

{

DEBUG("hello world %d %d\n", 1, 2);

return 0;

}

/**程序输出结果:

***********************************************************

File:E:\C Language\printf.c, Line:19, hello world 1 2

***********************************************************

*/

1、#用来把参数转换成字符.

2、##这个运算符把两个语言符号组合成单个语言符号

3、 __VA_ARGS__ 是一个可变参数的宏,实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。

4、##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错。

5、注意宏定义连接符  \  后面不要有任何操作,直接回车,下一行的前面可以有空格。

======================================================-=============================================

为更好了解C/C++中可变参数的知识,我从网上摘录了两篇文章,算是自己的一个总结。本篇主要是关于“## __VA_ARGS__”宏的介绍和使用。

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

1

#define pr_debug(fmt,arg...) \

2

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

#ifdef DEBUG

2

#define dbgprint(format,args...) \

3

fprintf(stderr, format, ##args)

4

#else

5

#define dbgprint(format,args...)

6

#endif

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

下面是C99的方法:

1

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

新的C99规范支持了可变参数的宏

具体使用如下:

以下内容为程序代码:

1

#include

2

#include

3

#define

LOGSTRINGS(fm, ...) printf(fm,__VA_ARGS__)

4

int main()

5

{

6

LOGSTRINGS("hello,

%d ",

10);

7

return 0;

8

}

但现在似乎只有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

#define

DEBUG(args) (printf("DEBUG: "), printf args)

2

if (n

!= 0) DEBUG(("n

is %d\n",

n));

明显的缺陷是调用者必须记住使用一对额外的括弧。

gcc 有一个扩展可以让函数式的宏接受可变个数的参数。 但这不是标准。另一种 可能的解决方案是根据参数个数使用多个宏 (DEBUG1, DEBUG2, 等等), 或者用逗号玩个这样的花招:

1

#define

DEBUG(args) (printf("DEBUG: "), printf(args))

2

#define

_ ,

3

DEBUG("i

= %d" _

i);

C99 引入了对参数个数可变的函数式宏的正式支持。在宏 “原型” 的末尾加上符号 … (就像在参数可变的函数定义中), 宏定义中的伪宏 __VA_ARGS__ 就会在调用是 替换成可变参数。

最后, 你总是可以使用真实的函数, 接受明确定义的可变参数

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

自定义调试信息的输出

调试信息的输出方法有很多种,  例如直接用printf,  或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进程则一般是使用syslog将调试信息输出到日志文件中等等...

使用标准的方法打印调试信息有时候不是很方便,  例如Qt编程, 在调试已有的代码时, 我想在打印调试信息的地方,

把代码位置也打印出来以方便定位错误, 或者需要在调试信息前面加一个前辍, 好方便在调试信息太多的时候可以用grep过滤一下,

仅显示本模块的调试信息, 这时就需要一个一个地修改已有的qDebug, 使其成为以下形式:

qDebug( "[模块名称] 调试信息  File:%s, Line:%d", __FILE__, __LINE__ );

这样的修改比较烦人, 而且一不小心会遗漏某个没改的...

为了能方便地管理调试信息的输出,一个比较简单的方法就是自已定义一个打印调试信息的宏,

然后替换原来的,废话就不多说了,直接给出一个现成的,下面是一个例子,

我用WiFi表示当前代码的模块名称,我要求在模块中的所有调试信息前面均带有[WiFi]前辍,这样我就能方便地只需使用命令行 | grep "

WiFi

"来过滤掉来自其它模块的调试信息了:

#define qWiFiDebug(format, ...) qDebug("[WiFi] "format" File:%s,

Line:%d, Function:%s", ##__VA_ARGS__, __FILE__, __LINE__ ,

__FUNCTION__);

上面的宏是使用qDebug输出调试信息,在非Qt的程序中也可以改为printf,守护进程则可以改为syslog等等...

其中,决窍其实就是这几个宏 ##__VA_ARGS__, __FILE__, __LINE__

和__FUNCTION__,下面介绍一下这几个宏:

1)  __VA_ARGS__

是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错,

你可以试试。

2) __FILE__ 宏在预编译时会替换成当前的源文件名

3) __LINE__宏在预编译时会替换成当前的行号

4) __FUNCTION__宏在预编译时会替换成当前的函数名称

有了以上这几个宏,特别是有了__VA_ARGS__ ,调试信息的输出就变得灵活多了。

有时,我们想把调试信息输出到屏幕上,而有时则又想把它输出到一个文件中,可参考下面的例子:

//debug.c

#include

#include

//开启下面的宏表示程序运行在调试版本, 否则为发行版本, 这里假设只有调试版本才输出调试信息

#define _DEBUG

#ifdef _DEBUG

//开启下面的宏就把调试信息输出到文件,注释即输出到终端

#define DEBUG_TO_FILE

#ifdef DEBUG_TO_FILE

//调试信息输出到以下文件

#define DEBUG_FILE "/tmp/debugmsg"

//调试信息的缓冲长度

#define DEBUG_BUFFER_MAX 4096

//将调试信息输出到文件中

#define printDebugMsg(moduleName, format, ...) {\

char buffer[DEBUG_BUFFER_MAX+1]={0};\

snprintf( buffer, DEBUG_BUFFER_MAX \

, "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );\

FILE* fd = fopen(DEBUG_FILE, "a");\

if ( fd != NULL ) {\

fwrite( buffer, strlen(buffer), 1, fd );\

fflush( fd );\

fclose( fd );\

}\

}

#else

//将调试信息输出到终端

#define printDebugMsg(moduleName, format, ...) \

printf( "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );

#endif //end for #ifdef DEBUG_TO_FILE

#else

//发行版本,什么也不做

#define printDebugMsg(moduleName, format, ...)

#endif  //end for #ifdef _DEBUG

int main(int argc, char** argv)

{

int data = 999;

printDebugMsg( "TestProgram", "data = %d", data );

return 0;

}

上面也说了,只有支持C99规范的gcc编译器才有__VA_ARGS__这个宏,如果不是gcc编译器,或者所用的gcc编译器版本不支持__VA_ARGS__宏怎么办? 可参考下面的代码片段,我们换一种做法,可先将可变参数转换成字符串后,再进行输出即可:

void printDebugMsg( const char* format, ...)

{

char buffer[DEBUG_BUFFER_MAX_LENGTH + 1]={0};

va_list arg;

va_start (arg, format);

vsnprintf(buffer, DEBUG_BUFFER_MAX_LENGTH, format, arg);

va_end (arg);

printf( "%s", buffer );

}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用:建议使用__VA_ARGS__时与##配合使用##__VA_ARGS__在设置log输出的宏定义时比较常见。 引用:程序开发或者看工程代码时可能在宏定义中有涉及到#、##、__VA_ARGS__和##__VA_ARGS__的使用,简单介绍下使用方法。#操作符是将其后面紧跟的符号变为字符串的形式。 #__VA_ARGS__是C语言宏定义中的一个特殊标识符,表示可变参数列表。它可以用来在宏定义中传递不定数量的参数。当我们在宏定义中使用#__VA_ARGS__时,它会被替换为传递给宏的所有参数,以逗号分隔的形式。这样,我们可以在宏定义中使用这些参数,类似于函数的参数。 而##__VA_ARGS__是在宏定义中使用可变参数列表时常见的形式。它的作用是在宏定义中使用可变参数列表时,可以将参数列表前的逗号省略掉。这样可以避免在宏定义中使用可变参数列表时出现额外的逗号。 所以,#__VA_ARGS__和##__VA_ARGS__都是在宏定义中使用可变参数列表时的常见用法,可以实现灵活的宏定义,提高代码的可读性和可维护性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [# ## __VA_ARGS__ ##__VA_ARGS__的使用说明](https://blog.csdn.net/hanxv_1987/article/details/106555870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值