1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used in the replacement section of a function-like macro.Additionally, it can be used in the replacement section of an object-like macro. The ## operator combines two tokens into a single token. ##将两个符号连接成一个。 For example, you could do this: #define XNAME(n) x ## n Then the macro XNAME(4) would expand to the following: x4 Listing 1 uses this and another macro using ## to do a bit of token gluing. // glue.c -- use the ## operator #include <stdio.h> #define XNAME(n) x ## n #define PRINT_XN(n) printf("x" #n " = %d\n", x ## n); int main(void) { int XNAME(1) = 14; // becomes int x1 = 14; int XNAME(2) = 20; // becomes int x2 = 20; PRINT_XN(1); // becomes printf("x1 = %d\n", x1); PRINT_XN(2); // becomes printf("x2 = %d\n", x2); return 0; } Here's the output: x1 = 14 x2 = 20 Note how the PRINT_XN() macro uses the # operator to combine strings and the ## operator to combine tokens into a new identifier. 2.Variadic Macros: ... and _ _VA_ARGS_ _ Some functions, such as printf(), accept a variable number of arguments. The stdvar.h header file,provides tools for creating user-defined functions with a variable number of arguments. And C99 does the same thing for macros.Although not used in the standard, the word variadic has come into currency to label this facility. (However, the process that has added stringizing and variadic to the C vocabulary has not yet led to labeling functions or macros with a fixed number of arguments as fixadic functions and normadic macros.) The idea is that the final argument in an argument list for a macro definition can be ellipses (that is, three periods)(省略号). If so, the predefined macro _ _VA_ARGS_ _ can be used in the substitution part to indicate what will be substituted for the ellipses. For example, consider this definition: #define PR(...) printf(_ _VA_ARGS_ _) Suppose you later invoke the macro like this: PR("Howdy"); PR("weight = %d, shipping = $%.2f\n", wt, sp); For the first invocation, _ _VA_ARGS_ _ expands to one argument: "Howdy" For the second invocation, it expands to three arguments: "weight = %d, shipping = $%.2f\n", wt, sp Thus, the resulting code is this: printf("Howdy"); printf("weight = %d, shipping = $%.2f\n", wt, sp); Listing 2 shows a slightly more ambitious example that uses string concatenation and the # operator: // variadic.c -- variadic macros #include <stdio.h> #include <math.h> #define PR(X, ...) printf("Message" #X ": " _ _VA_ARGS_ _) int main(void) { double x = 48; double y; y = sqrt(x); PR(1, "x = %g\n", x); PR(2, "x = %.2f, y = %.4f\n", x, y); return 0; } In the first macro call, X has the value 1, so #X becomes "1". That makes the expansion look like this: (#为参数加双引号。) print("Message " "1" ": " "x = %g\n", x); Then the four strings are concatenated, reducing the call to this: print("Message 1: x = %g\n", x); Here's the output: Message 1: x = 48 Message 2: x = 48.00, y = 6.9282 Don't forget, the ellipses have to be the last macro argument: #define WRONG(X, ..., Y) #X #_ _VA_ARGS_ _ #y(这个是错误的例子。)
建议你看看C语言相关的预处理命令部分 对于这 #define 是宏定义命令,分为无参数宏定义 和 有参数宏定义,你这个属于有参数的宏定义; 对于有参数的宏定义,参数部分 应该为 要替换为的部分的变量; 你这里 #define dbg_msg(...) 参数部分 是 ... 没有这种用法 例如: #include<stdio.h> #define dbg_msg(__FUNCTION__,__LINE__,__VA_ARGS__) {printf("### [%s:%d] ", __FUNCTION__, __LINE__); printf(__VA_ARGS__);} void main() { char a[] = "123"; int b = 5; char c[]="456"; dbg_msg(a,b,c); //宏定义要替换的部分,在编译前预处理器就会将这个地方替换为 目标字符串,之后才进行编译 }
上面的宏是使用qDebug输出调试信息,在非Qt的程序中也可以改为printf,守护进程则可以改为syslog等等... 其中,决窍其实就是这几个宏 ##__VA_ARGS__, __FILE__, __LINE__ 和__FUNCTION__,下面介绍一下这几个宏:
#、##和__VA_ARGS__1.#
较大的项目都会用大量的宏定义来组织代码,你可以看看/usr/include下面的头文件中用 了多少个宏定义。看起来宏展开就是做个替换而已,其实里面有比较复杂的规则,C语言有很多复杂但不常用的语法规则本书并不涉及,但有关宏展开的语法规则本 节却力图做全面讲解,因为它很重要也很常用。
#define MAX(a, b) ((a)>(b)?(a):(b)) 我们想看第二行的表达式展开成什么样,可以用gcc的-E选项或cpp命令,尽管这个C程序不合语法,但没关系,我们只做预处理而不编译,不会检查程序是否符合C语法。
$ cpp main.c
就像函数调用一样,把两个实参分别替换到宏定义中形参a和b的位置。注意这种函数式宏定义和真正的函数调用有什么不同:
#define MAX(a, b) ((a)>(b)?(a):(b))
这段代码从一个数组中找出最大的数,如果MAX是个真正的函数,这个算法就是从前到后遍历一遍数组,时间复杂度是Θ(n),而现在MAX是这样一个函数式宏定义,思考一下这个算法的时间复杂度是多少?
#define device_init_wakeup(dev,val) \
为什么要用do { ... } while(0)括起来呢?不括起来会有什么问题呢?
#define device_init_wakeup(dev,val) \
这样宏展开之后,函数体的第二条语句不在if条件中。那么简单地用{ ... }括起来组成一个语句块不行吗?
#define device_init_wakeup(dev,val) \
问题出在device_init_wakeup(d, v);末尾的;号,如果不允许写这个;号,看起来不像个函数调用,可如果写了这个;号,宏展开之后就有语法错误,if语句被这个;号结束掉了,没法跟 else配对。因此,do { ... } while(0)是一种比较好的解决办法。
#define OBJ_LIKE (1 - 1)
在定义的前后多些空白(这里的空白包括空格、Tab、注释,因为前一步预处理要把注释替换成空格)没有关系,在定义中间连续多个空白等价于一个空白,但在定义中间有空白和没有空白被认为是不同的,所以这样的重复定义是不允许的:
#define OBJ_LIKE (1 - 1)
如果需要重新定义一个宏,和原来的定义不同,可以先用#undef取消原来的定义,再重新定义,例如:
2.2. 内联函数
static inline void down_read(struct rw_semaphore *sem)
inline关键字告诉编译器,这个函数的调用要尽可能快,可以当普通的函数调用实现,也可以用宏展开的办法实现。我们做个实验,把上一节的例子改一下:
inline int MAX(int a, int b)
$ gcc main.c -g
可以看到MAX是作为普通函数调用的。如果指定优化选项编译,然后反汇编: $ gcc main.c -g -O
#define STR(s) # s
用cpp命令预处理之后是"hello?world",自动用"号把实参括起来成为一个字符串,并且实参中的连续多个空白字符被替换成一个空格。
#define STR(s) #s 预处理之后是fputs("strncmp("ab\"c\0d", "abc", '\4"') == 0" ": @n", s);,注意如果实参中包含字符常量或字符串,则宏展开之后字符串的界定符"要替换成",字符常量或字符串中的和"字符要替换成\和"。
#define CONCAT(a, b) a##b
预处理之后是concat。再比如,要定义一个宏展开成两个#号,可以这样定义:
#define HASH_HASH # ## #
中间的##是运算符,宏展开时前后两个#号被这个运算符连接在一起。注意中间的两个空格是不可少的,如果写成####,会被划分成##和##两个Token,而根据定义##运算符用于连接前后两个预处理Token,不能出现在宏定义的开头或末尾,所以会报错。
#define showlist(...) printf(#__VA_ARGS__) 预处理之后变成: printf("The first, second, and third items."); 在宏定义中,可变参数的部分用__VA_ARGS__表示,实参中对应...的几个参数可以看成一个参数替换到宏定义中__VA_ARGS__所在的地方。 #define FOO() foo 预处理之后变成foo。FOO在定义时不带参数,在调用时也不允许传参数给它。
#define FOO(a) foo##a
预处理之后变成:
foobar
FOO在定义时带一个参数,在调用时必须传一个参数给它,如果不传参数则表示传了一个空参数。
#define FOO(a, b, c) a##b##c
预处理之后变成:
123
FOO在定义时带三个参数,在调用时也必须传三个参数给它,空参数的位置可以空着,但必须给够三个参数,FOO(1,2)这样的调用是错误的。 #define FOO(a, ...) a##__VA_ARGS__ 预处理之后变成:
FOO(1)这个调用相当于可变参数部分传了一个空参数,FOO(1,2,3,)这个调用相当于可变参数部分传了三个参数,第三个是空参数。
#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)
printk这个内核函数相当于printf,也带有格式化字符串和可变参数,由于内核不能调用libc的函数,所以另外实现了一个打印函数。这个 函数式宏定义可以这样调用:DEBUGP("info no. %d", 1)。也可以这样调用:DEBUGP("info")。后者相当于可变参数部分传了一个空参数,但展开后并不是printk("info",),而是 printk("info"),当__VA_ARGS是空参数时,##运算符把它前面的,号“吃”掉了。
#define sh(x) printf("n" #x "=%d, or %d\n",n##x,alt[x])
sh(sub_z)要用sh(x)这个宏定义来展开,形参x对应的实参是sub_z,替换过程如下:
展开的步骤是: 可变参数宏
在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:
#define pr_debug(fmt,arg...) \ printk(KERN_DEBUG fmt,##arg) 用可变参数宏(variadic macros)传递可变参数表 你可能很熟悉在函数中使用可变参数表,如: void printf(const char* format, …); 直到最近,可变参数表还是只能应用在真正的函数中,不能使用在宏中。 C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(variadic macros),这样你就可以使用拥有可以变化的参数表的宏。可变参数宏就像下面这个样子: #define debug(…) printf(__VA_ARGS__) 缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__ 把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。例如: Debug(“Y = %d\n”, y); 而处理器会把宏的调用替换成: printf(“Y = %d\n”, y); 因为debug()是一个可变参数宏,你能在每一次调用中传递不同数目的参数: debug(“test”); //一个参数 可变参数宏不被ANSI/ISO C++ 所正式支持。因此,你应当检查你的编译器,看它是否支持这项技术。
用GCC和C99的可变参数宏, 更方便地打印调试信息gcc的预处理提供的可变参数宏定义真是好用: #ifdef DEBUG如此定义之后,代码中就可以用dbgprint了,例如dbgprint("aaa %s", __FILE__);。感觉这个功能比较Cool :em11: 下面是C99的方法: #define dgbmsg(fmt,...) \ 新的C99规范支持了可变参数的宏 具体使用如下: 以下内容为程序代码: #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 标准中,宏可以象函数一样,定义时可以带有可变参数。宏的语法和函数的语法类似。下面有个例子: #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参数一样,这些参数不是宏的扩展。 怎样写参数个数可变的宏一种流行的技巧是用一个单独的用括弧括起来的的 ``参数" 定义和调用宏, 参数在 宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。 #define DEBUG(args) (printf("DEBUG: "), printf args) 明显的缺陷是调用者必须记住使用一对额外的括弧。 gcc 有一个扩展可以让函数式的宏接受可变个数的参数。 但这不是标准。另一种 可能的解决方案是根据参数个数使用多个宏 (DEBUG1, DEBUG2, 等等), 或者用 逗号玩个这样的花招: #define DEBUG(args) (printf("DEBUG: "), printf(args)) C99 引入了对参数个数可变的函数式宏的正式支持。在宏 ``原型" 的末尾加上符号 ... (就像在参数可变的函数定义中), 宏定义中的伪宏 __VA_ARGS__ 就会在调用是 替换成可变参数。 最后, 你总是可以使用真实的函数, 接受明确定义的可变参数如果你需要替换宏, 使用一个 函数和一个非函数式宏, 如 #define printf myprintf。 其实如果不怕出现no effect statement warning的话,定义成
#ifdef _DEBUG
#define _DEBUGOUT printf #else #define _DEBUGOUT #endif 如果支持可变参数的话,定义成
#ifdef _DEBUG
#define _DEBUGOUT printf #else #define _DEBUGOUT(x, ...) #endif 关于##在C宏定义中的作用
最近因为工作问题,一直要看Linux的源代码。对于源码中宏定义的#一直有点疑惑,发现一个哥们总结的不错,所以就Ctrl + C and Ctrl + V进来: 内核中有很多的宏定义,在宏定义define中经常看到两个字符串##和#,这里把它的用法做一下说明: 另外,如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开 。 #define STRCPY(a, b) strcpy(a ## _p, #b) /* 注意这里 */ ///////////////////////////////////////////////////////////////////////// tell you about ## in common text 1. 简单的说,“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。 其中,分隔的作用类似于空格。我们知道在普通的宏定义中,预处理器一般把空格 另外一些分隔标志是,包括操作符,比如 +, -, *, /, [,], ...,所以尽管下面的 而其强制连接的作用是,去掉和前面的字符串之间的空格,而把两者连接起来。
#define A1(name, type) type name_##type##_type 或 A1(a1, int); /* 等价于: int name_int_type; */ 解释: 2) 而在第二个宏定义中,“name”和第一个“_”之间也被分隔了,所以 3) A1和A2的定义也可以如下: 结果是## 会把前面的空格去掉完成强连接,得到和上面结果相同的宏定义 3. 其他相关 -- 单独的一个 # 至于单独一个#,则表示 对这个变量替换后,再加双引号引起来。比如 #define __stringify_1(x) #x 所以,对于MODULE_DEVICE_TABLE 1) #define MODULE_DEVICE_TABLE(type,name) 得到 注意到alias attribute需要一个双引号,所以在这里使用了__stringify(name)来 4. 分析方法和验证方式 -- 编写一个简单的C程序 用宏定义一个变量,同时用直接方式定义一个相同的变量,编译报告重复定义;
我看《APUE》的时候信号那一章有这样的宏定义:我想知道(void (*)())-1 这是 > #define SIG_ERR (void (*)())-1 > #define SIG_IGN (void (*)())1 这个纯粹是C语言问题。 写成这样可能会好理解一点: #define SIG_ERR (sig_handler_prototype)-1 typedef void (*sig_handler_prototype)(int);
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bluecll/archive/2008/11/09/3254764.aspx # define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)#else//usr space # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) ##args表示如果args为空则消除前面的逗号 define小结 ajumail 发表于 2006-11-10 1. 定义简单的常数:定义常量,便于修改(切不可在后面加上分号!) 2. 定义简单的函数:注意多使用括号 3. 定义单行宏:主要有以下三种用法. 4. 定义多行宏:注意斜杠的使用,最后一行不能用斜杠. 5. 用于条件编译:(常用形式) 6. 一些注意事项: |
|