当前位置:  编程技术>c/c++/嵌入式

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

    来源: 互联网  发布时间:2014-10-20

    本文导语:  编写代码的过程中,经常会输出一些调试信息到屏幕上,一般会调用printf这类的函数。但是当调试解决之后,我们需要手工将这些地方删除或者注释掉。最近在看《Linux C编程一站式学习》这本书,就想到一个方法: 代码如下:v...

编写代码的过程中,经常会输出一些调试信息到屏幕上,一般会调用printf这类的函数。
但是当调试解决之后,我们需要手工将这些地方删除或者注释掉。
最近在看《Linux C编程一站式学习》这本书,就想到一个方法:

代码如下:

void myprintf(char* fmt, ...)
{
}
#ifdef DEBUG
#define printf(fmt, args...) myprintf(fmt, ##args)
#endif

调试阶段带着DEBUG调试,正式上线就可以把printf变成一个空函数了。
这样做的一个潜在风险是可能会导致默写glib函数需要调用printf输出错误log也给取消掉了。
令人欣慰的是,大部分glib调用的应该是fprintf。

虽然问题解决了,但是我对args...以及##args还是不太了解。上网找了些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参数一样,这些参数不是宏的扩展。

具体参见《Linux C编程一站式学习》,顺便夸赞下这本书,写的很好!


    
 
 

您可能感兴趣的文章:

  • 如何定义一个可变参数的自定义函数
  • PHP变量的定义、可变变量、变量引用、销毁方法
  • c/c++中变量的声明和定义深入解析
  • 解析C++浮点数无效值的定义与无效值判定的小结
  • 内联函数inline与宏定义深入解析
  • shell自定义函数及参数调用解析
  • Visual C#类的定义及实现方法实例解析
  • 变量定义与声明的区别详细解析
  • 深入解析c#中枚举类型的定义与使用
  • C#定义并实现单链表实例解析
  • 解析C#自定义控件的制作与使用实例的详解
  • 解析Android中使用自定义字体的实现方法
  • C语言中的内联函数(inline)与宏定义(#define)详细解析
  • 头文件不宜定义变量的原因全面解析
  • 共用体的定义与应用详细解析
  • 解析结构体的定义及使用详解
  • 解析在Android中为TextView增加自定义HTML标签的实现方法
  • Android自定义属性 format的深入解析
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • java中函数的缺省参数怎么定义?
  • make 命令可不可以接受自定义的参数?
  • GCC编译程序时,有自定义头文件,提示找不到该头文件,但他就在这个目录中,如何设置GCC参数?
  • Python def函数的定义、使用及参数传递实现代码
  • 急:unix的shell脚本中,定义了一个函数getDate(),在后面的执行命令参数用要使用这个函数的返回值,应该怎么写?
  • CSS学习笔记Padding 属性中参数的定义与使用
  • scouinx 下的 ftw(char *path, int (*fn)(), int depth)的第2个参数应该怎样定义和使用?
  • C语言基础问题,参数定义在函数名后面,是为什么?
  • 制作RPM包时如何添加自定义参数?
  • Shell脚本函数定义和函数参数
  • Python自定义函数的创建、调用和函数的参数详解
  • java命名空间java.awt类radialgradientpaint的类成员方法: getradius定义及介绍
  • 请问可以用宏定义定义一个二维数组吗?
  • java命名空间javax.xml.stream类xmlinputfactory成员方法: seteventallocator定义参考
  • 想修改路径定义,想找到在哪里定义的,应该怎么办?
  • java命名空间java.text接口attributedcharacteriterator的类成员方法: getallattributekeys定义及介绍
  • php定义数组和使用示例(php数组的定义方法)
  • java命名空间java.beans接口propertyeditor的类成员方法: supportscustomeditor定义及介绍
  • 上次问题解决了,原来是extern被重定义了。。。天阿,关键字也可以被重定义。。。。。欢迎接分
  • java命名空间javax.swing类jtextarea的类成员方法: getcolumnwidth定义及介绍
  • 下面的变量我都下了定义,为什么还出现没定义的信息
  • java命名空间javax.swing类jtextfield的类成员方法: getcolumnwidth定义及介绍
  • oracle异常(预定义异常,自定义异常)应用介绍
  • java命名空间javax.swing类actionmap的类成员方法: allkeys定义及介绍
  • 为什么我在头文件time.h里找不到time_t的定义呢?只有这个定义typedef __time_t time_t;
  • java命名空间javax.swing类inputmap的类成员方法: allkeys定义及介绍
  • 函数有定义怎么提示没有低能定义的错误呢?
  • java命名空间javax.rmi.corba接口valuehandler的类成员方法: iscustommarshaled定义及介绍
  • android自定义控件和自定义回调函数步骤示例
  • java命名空间javax.swing类jcomponent的类成员方法: resetkeyboardactions定义及介绍
  • 自定义公共类的装载,包的定义


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3