?? C语言博大精深,每一个细节都蕴含者着很多技巧特性,相信C程序员的同行首次遇到"#","#@"和"##"之类的技巧时也是不知所云。本文总结"#","#@"和"##"在宏定义中的使用技巧,收集于网络!以备需要时查阅,并希望能帮助其他同行。
一、一般用法
#把宏参数变为一个字符串,#@把宏参数变为一个字符,##把两个宏参数贴合在一起。
?#include
#include
#define STR(s)??????? #s??????????????????? // #与参数之间可以有空格
#define TOCHAR(c)?????? #@c?????????
#define CONS(a,b)? int(a##e##b)???? // ##与参数之间可以有空格
int main(void)
{
??? printf(STR(pele));?????????????? // 输出字符串"pele"
??? printf("%c\n",TOCHAR(z));? // 输出字符z
??? printf("%d\n",CONS(2,3));?? // 2e3 输出:2000
??? return 0;
}
二、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开的。
#define A?????????????? (2)
#define STR(s)??????? #s
#define CONS(a,b)? int(a##e##b)
?
printf("int max: %s\n", STR(INT_MAX));
这行会被展开为:
printf("int max: %s\n","INT_MAX");
printf("%s\n", CONS(A, A));?
这一行被展开为:
printf("%s\n", int(AeA));
?
INT_MAX和A都不会再被展开,然而解决这个问题的方法很简单,多加一层中间转换宏。加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数。
#define A???????????????? (2)
#define _STR(s)??????? #s
#define STR(s)???????? _STR(s)??????????????? //转换宏
#define _CONS(a,b)? int(a##e##b)
#define CONS(a,b)??? _CONS(a,b)???????? //转换宏
?
printf("int max: %s\n",STR(INT_MAX));
输出为:int max: 0x7fffffff
STR(INT_MAX)? --> _STR(0x7fffffff)? --> "0x7fffffff"
?
printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A)? -->? _CONS((2), (2)) --> int((2)e(2))
?
以下为Minix操作系统相关的源代码:
#ifdef?? _ANSI
#define?? __str(x)?????? # x
#define??? __xstr(x)??? __str(x)??????? //转换宏
_PROTOTYPE( void __bad_assertion, (const char*_mess) );
#define?? assert(expr)((expr)?(void)0 : \
???????????????????????????????? __bad_assertion("Assertion \"" #expr \
????????????????????????????????? "\" failed, file " __xstr(__FILE__) \
????????????????????????????????? ", line " __xstr(__LINE__) "\n"))
三、"#"和"##"的一些应用特例
1、合并匿名变量名
#define? ___ANONYMOUS1(type, var, line)??? type? var##line
#define? __ANONYMOUS0(type, line)???????????? ___ANONYMOUS1(type, _anonymous, line)
#define? ANONYMOUS(type)???????????????????????? __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int);? 即: static int _anonymous70;? 70表示该行行号;
第一层:ANONYMOUS(static int);? -->? __ANONYMOUS0(staticint, __LINE__);
第二层:?????????????????????? -->? ___ANONYMOUS1(static int, _anonymous,70);
第三层:?????????????????????? -->? static int? _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;
?
2、填充结构
#define? FILL(a)?? {a, #a}
enum IDD{OPEN, CLOSE};
typedef struct MSG
{
?? IDD id;
?? const char * msg;
}MSG;
MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, "OPEN"}, {CLOSE,"CLOSE"}};
?
3、记录文件名
#define? _GET_FILE_NAME(f)?? #f
#define? GET_FILE_NAME(f)?? _GET_FILE_NAME(f)?????? //转换宏
static char? FILE_NAME[] =GET_FILE_NAME(__FILE__);
?
4、得到一个数值类型所对应的字符串缓冲大小
#define? _TYPE_BUF_SIZE(type)? sizeof#type
#define? TYPE_BUF_SIZE(type)? _TYPE_BUF_SIZE(type)
char? buf[TYPE_BUF_SIZE(INT_MAX)];
???? -->? char buf[_TYPE_BUF_SIZE(0x7fffffff)];
???? -->? char buf[sizeof "0x7fffffff"];
这里相当于:
char? buf[11];
?