Chinaunix首页 | 论坛 | 博客
  • 博客访问: 47254
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 106
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-11 11:04
文章分类

全部博文(17)

文章存档

2014年(17)

我的朋友

分类: LINUX

2014-02-12 18:20:12

问题:下面通过宏定义实现一个可以指定前缀的字符串。
PREFIX+".%d"

方法1:使用#运算符。出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。

  1. #include  
  2. #define PREX 1.3.6  
  3. #define FORMAT(n) #n".%d\n"  
  4.   
  5. int main()  
  6. {  
  7.     int ival = 246;  
  8.     printf(FORMAT(PREX), ival);// PREX.246  
  9.       
  10.     return 0;  
  11. }  


但是输出结果是:PREX.246,和预期的结果不一样,宏PREX作为宏FORMAT的参数并没有替换。那么如何让FORMAT宏的参数可以替换呢?
首先,C语言的宏是允许嵌套的,其嵌套后,一般的展开规律像函数的参数一样:先展开参数,再分析函数,即由内向外展开。但是,注意:
(1) 当宏中有#运算符时,参数不再被展开;
(2) 当宏中有##运算符时,则先展开函数,再展开里面的参数;

PS:
##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号(非字符串)。


方法2:修改宏定义的格式,再添加一个中间宏TMP(x)实现对参数的替换,然后再替换为最终想要的字符串。


  1. #define PREX 1.3.6  
  2. #define FORMAT(x) TMP(x)  
  3. #define TMP(x) #x".%d\n"  
  4.   
  5. int main()  
  6. {  
  7.     int ival = 246;  
  8.     printf(FORMAT(PREX), ival);// 1.3.6.246  
  9.       
  10.     return 0;  
  11. }  

嵌套宏在某些情况下还是有一定的用处,但是我可能更愿意定义一个函数宏来完成上面这个工作:



  1. #include   
  2. #define FORMAT_FUN(szPrex, szFormat) do { \  
  3.     char szTmp[128] = {0}; \  
  4.     _snprintf(szTmp, sizeof(szTmp)-1, "%s", szFormat); \  
  5.     _snprintf(szFormat, sizeof(szFormat)-1, "%s%s", szPrex, szTmp); \  
  6. while(0); \  
  7.   
  8. const char *szPrex = "1.3.6";  
  9.   
  10. int main()  
  11. {  
  12.     int ival = 246;  
  13.     char szFormat[128] = ".%d\n";  
  14.     FORMAT_FUN(szPrex, szFormat);  
  15.       
  16.     //printf("%s\n", szFormat);  
  17.     printf(szFormat, ival);// 1.3.6.246  
  18.   
  19.     return 0;  
  20. }  

举几个关于宏嵌套用法的例子:


Ex. 1


  1. #include   
  2. #define TO_STRING2(x) #x  
  3. #define TO_STRING(x) TO_STRING1(x)  
  4. #define TO_STRING1(x) #x  
  5. #define PARAM(x) #x  
  6. #define ADDPARAM(x) INT_##x  
  7.   
  8. int main()  
  9. {  
  10.     const char *str = TO_STRING(PARAM(ADDPARAM(1)));  
  11.     printf("%s\n",str);  
  12.   
  13.     str = TO_STRING2(PARAM(ADDPARAM(1)));  
  14.     printf("%s\n",str);  
  15.   
  16.     return 0;  
  17. }  
  18. /* 
  19. output: 
  20. "ADDPARAM(1)" 
  21. PARAM(ADDPARAM(1)) 
  22. */  

Ex. 2



  1. #include   
  2. #define TO_STRING2(x) a_##x  
  3. #define TO_STRING(x) TO_STRING1(x)  
  4. #define TO_STRING1(x) #x  
  5. #define PARAM(x) #x  
  6. #define ADDPARAM(x) INT_##x  
  7.   
  8. int main()  
  9. {  
  10.     const char *str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));  
  11.     printf("%s\n",str);  
  12.   
  13.     return 0;  
  14. }  
  15. /* 
  16. VS2010 output: 
  17. a_PARAM(ADDPARAM(1)) 
  18.  
  19. GCC 4.3.2 output: 
  20. a_PARAM(INT_1) 
  21. */  

注意:例子2的代码分别在不同的编译器上输出结果不相同。这是为什么呢?

C99_TC3
6.10.3.1 Argument substitution
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. Aparameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument's preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
除非替换序列中的形式参数的前面有一个#符号,或者其前面或后面有一个##符号,否则,在插入前要对宏调用的实际参数记号进行检查,并在必要时进行扩展。

改为:



  1. #include   
  2. #define TO_STRING2(x) a_##x  
  3. #define TO_STRING(x) TO_STRING1(x)  
  4. #define TO_STRING1(x) T(x)  
  5. #define T(x) #x  
  6. #define PARAM(x) #x  
  7. #define ADDPARAM(x) INT_##x  
  8.   
  9. int main()  
  10. {  
  11.     const char *str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));  
  12.     printf("%s\n",str);  
  13.   
  14.     return 0;  
  15. }  
  16. /* 
  17. VS2010 output: 
  18. a_PARAM(INT_1) 
  19.  
  20. GCC 4.3.2 output: 
  21. a_PARAM(INT_1) 
  22.  
  23. (1) 对TO_STRING的参数TO_STRING2(...)进行检查替换,生成标记a_PARAM(ADDPARAM(1)) 
  24. (2) 对TO_STRING1的参数a_PARAM(ADDPARAM(1))进行检查替换,生成标记a_PARAM(INT_1) 
  25. (3) 对T的参数a_PARAM(INT_1)进行检查替换,生成字符串"a_PARAM(INT_1)" 
  26. */  

Ex. 3



  1. #include   
  2.   
  3. int ival = 0;  
  4. #define A(x) printf("%d\n", ival+=1);  
  5. #define B(x) printf("%d\n", ival+=2);  
  6. #define C() printf("%d\n", ival+=3);  
  7.   
  8.   
  9. int main()  
  10. {  
  11.     A(B(C()));  
  12.     printf("%d\n", ival);// ?, 1  
  13.   
  14.     return 0;  
  15. }  




补充知识:
The C Programming Language, Second Edition
P.205 预处理

(1) 预处理器执行宏替换、条件编译以及包含指定的文件。
(2) 以#开头的命令行("#"前可以有空格),就是预处理器处理的对象。
(3) 这些命令行的语法独立于语言的其他部分,它们可以出现在任何地方,其作用可延续到所在翻译单元的末尾(与作用域无关)。
(4) 行边界是有实际意义的。每一行都将单独进行分析。

阅读(1987) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~