您的位置:首页 > 编程语言 > C语言/C++

[c语言]宏中##的用法(续)

2015-07-22 09:48 218 查看
如下代码,不明觉厉。

#   define ext2_debug(f, a...)  { \
                     printk ("EXT2-fs DEBUG (%s, %d): %s:", \
                         __FILE__, __LINE__, __func__); \
                     printk (f, ## a); \
                     }

特别是其中printf(f, ## a);这样的用法。

Google之后在stackoverflow找到了这个问题以及解答。

链接:http://stackoverflow.com/questions/13225921/in-printk-what-does-mean

详细的解答跳转到了GNU的gcc文档:

原文链接:https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

译文如下:

3.6 Variadic Macros (可变参数的宏)

一个宏可以像一个函数一样,接收数量可变的参数。这样的语法在宏中的定义与在函数中的定义是类似的。

这里是一个例子。

        #define eprintf(...) fprintf (stderr, __VA_ARGS__)

这种宏被称为variadic.。当这种宏被调用时,参数列表中在最后一个被命名参数之后的所有字段,包括所有逗号,都变为可变的参数(variable argument)。标识符__VA_ARGS__无论出现在宏的什么位置,都将被这一系列的字段替换掉。

因此,我们可以得到这样的表达式。

        eprintf ("%s:%d: ", input_file, lineno)

          ==>  fprintf (stderr, "%s:%d: ", input_file, lineno)

可变参数在插入到宏的表达式之前,是一个完整的宏的扩展,就像普通的参数一样。你可以使用#将操作符转化为字符串,也可以使用##将多个字段连接为一个字段。(但是,如下将展示##的一个重要的特别用法。)

 如果你的宏是复杂的,对于可变的参数,你应该想要一个比__VA_ARGS__更具有表达性的名字。作为一个表达方式,CPP允许这一点。你可以在“...”之前写一个参数的名字,用于表达这个参数是可变参数。

之前的eprintf宏可以使用这样的表达方式被写为:

           #define eprintf (args...) fprintf (stderr, args)

注意,不可以在同一个宏中同时使用这种表达方式和__VA_ARGS__。

你也可以使用__VA_ARGS__在一个可变参数的宏中来表达可变的参数。

我们将像这样来重新定义eprintf。

        #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

这样的方式看起来更加具有可读性,但是这样的方式缺乏适应性:在格式化的参数之后,我们必须提供至少一个参数。在标准C中,你不能在命名参数和可变参数之间省略逗号。而且,如果你保留可变参数为空,你将得到一个编译错误,因为在格式化参数的后面将多出一个逗号。

        eprintf("success!\n", );

          ==> fprintf(stderr, "success!\n", );

GNU CPP提供两种表达式,可以处理这个问题。

第一种,你将被允许不添加整个可变的参数

        eprintf("success!\n" );

          ==> fprintf(stderr, "success!\n", );

第二种,字段连接操作符##被放在一个逗号和一个可变的参数之间时,具有一种特殊的意义。

如果你写了如下表达式:

        #define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

当eprintf这个宏被调用时,可变的参数被省略,然后##之前的逗号被删除。如果输入一个空的参数,这个过程将不会发生;如果##之前的字段不是逗号,这个过程也不会发生。

        eprintf("success!\n");

          ==> fprintf(stderr, "success!\n");

====(未完待续,睡觉先)====

========接上回分解=======

以上的解释是针对一种情况,唯一的宏参数为一个可变变量的参数。这样的解释是含糊不清的。而尝试区分没有参数的情况是否是一个空参数或者一个错误参数是没有意义的。在这种情况下,C99标准清楚的表明逗号必须保留,然而现存GCC扩展版习惯于吞掉逗号。所以当CPP依照C99标准时,CPP保留逗号 ,而其他的则删除逗号。

C99提出来唯一的一处要求:标识符__VA_ARGS__能够出现在一个可变变量宏的替换列表里。但是这个标识符不可以作为一个宏的名字,宏的参数名,或者在一个不同类型的宏中。这个表示也禁止在open text中使用;标准是不明确的。我们推荐避免它的其他用法,除了它被定义用法。(open text是什么,我也没明白,Google也没找到合理解释。)

可变变量的宏是一个C99中的新功能。GNU CPP 已经支持这个功能很长时间,但是只支持一钟可变参数的命名(支持‘args...’;不支持'...' 和 __VA_ARGS__)。如果你考虑对GCC之前版本的兼容性,那么你应当只使用被命名的可变变量'args...'。另一方面,如果你考虑依照C99的实现,你应当只使用__VA_ARGS__。

之前版本的CPP实现了更加通用的针对逗号删除的扩展。在此份文档中,我们限制了这一点,为了使得与C99的差异最小。为了在此版GCC和之前的GCC版本中获得同样的作用,‘##’之前的字段必须是一个逗号,并且在逗号和逗号之前的任何字段之间必须有一个空格:

         #define eprintf(format, args...) fprintf (stderr, format , ##args)

======翻译部分完结======
C的标准之间,也是略有差异的。有一些特别的写法,可能是某个标准的特殊用法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: