广泛用于C语言程序中,本文总结了宏的分类, 作用与使用注意事项  


 


 宏定义分类:  


 


 (1)不带参数的宏定义  


 


 形式: #define 宏名 [宏体]  


 


 功能:可以实现用宏体代替宏名  


 


 使用实例: #define TRUE 1  



 作用:程序中多次使用TRUE,如果需要对TRUE的值进行修改,只需改动一处就可以了  



 (2)带参数的宏: #define 宏名 ( 参数表) [宏体]  


 


 宏定义作用:  


 


 (1)方便程序的修改  


 


 上面的#define TRUE 1就是一个实例  


 


 (2)提高程序的运行效率  


 


 宏定义的展开是在程序的预处理阶段完成的,无需运行时分配内存,能够部分实现函数的功能,却没有函数调用的压栈、弹栈开销,效率较高  


 

(3)增强可读性  

 

 


 这点不言而喻,当我们看到类似PI这样的宏定义时,自然可以想到它对应的是圆周率常量  


 


 (4)字符串拼接  


 

    

 

   例如: 

 

  #define CAT(a,b,c) a##b##c 

 

    

 

  main() 

 

  { 

 

      printf("%d\n" CAT(1,2,3)); 

 

      printf("%s\n", CAT('a', 'b', 'c'); 

 

  } 

 

    

 

  程序的输出会是: 

 

    

 

  123 

 

  abc 

 

    

 

    

 

  (5)参数转化成字符串 

 

    

 

  示例: 

 

    

 

  #defind CAT(n) "abc"#n 

 

    

 

  main() 

 

  { 

 

      printf("%s\n", CAT(15)); 

 

  } 

 

    

 

  输出的结果会是 

 

  abc15 

 

    

 

  (6)用于程序调试跟踪 

 
 常见的用于调试的宏有,_ L I N E _,_ F I L E _,_ D A T E _,_ T I M E _,_ S T D C _
 (7)实现可变宏
 举例来说:
 #define PR(...) printf(_ _VA_ARGS_ _)
 
 
 
 

  使用宏定义中常见的注意事项有: 

 

    

 

  (1)不要为宏定义加分号 
 
 

  宏定义在预处理阶段只是进行字符串替换 

 

    

 

  例如: #define PI 3.14159 

 

    

 

  float square = PI*r*r; 

 

    

 

  如果此时将PI加上分号,显然会编译出错 

 

    

 

  (2)为了防止出现意想不到的替换结果,对于带参数的宏,最好对每个参数和宏都加上配对的括号 

 

    

 

  例如: #define MUL(a,b) a*b 

 

  此时如果这样来使用 a=MUL(2+3, 4+5); 

 

  替换后的结果是a=2+3*4+5。结果变成了19,而不是我们期望的45 

 

    

 

  为了解决这个问题,我们为改进一下 

 

  #define MUL(a,b) (a)*(b) 

 

  上面的例子没有问题了,但还有下面的情况处理不了 

 

    

 

  #define SUB(a,b) (a)-(b) 

 

    

 

  当我们调用 int x = 10*SUB(8,2) 

 

    

 

  展开后的结果是x=10*(8)-2=72,而不是我们所期望的60 

 

    

 

  因此比较好的写法是 

 

    

 

   #define SUB(a,b) ((a)-(b)) 
 
 
 

     
 
 
 

   (3)防止参数多次取值的错误 
 
 
 

     
 
 
 

   这个错误比较隐蔽一些,通过例子来说明 
 
 
 

     
 
 
 

   #define MIN(a,b) ((a) > (b) ? (a) : (b)) 
 
 
 

     
 
 
 

   如果我们这样来使用 
 
 
 

     
 
 
 

   int x=1; 
 
 
 

   int y=2; 
 
 
 

   int z=MIN(x++,y++); 
 
 
 

   int k=MIN(y++,x++); 
 
 
 

     
 
 
 

   z展开后是 int z=((x++) > (y++) ? (x++) : (y++)); 
 
 
 

   k展开后是 int z=((y++) > (x++) ? (y++) : (x++)); 
 
 
 

     
 
 
 

   MIN宏设计的初衷是希望得到x,y的较小值,z和k都应该返回1 
 
 
 

   但结果却是z=2,k=3 
 
 
 

     
 
 
 

   同样,如果宏的一个参数是一个函数,也会出现函数被多次执行的情况 
 
 
 

     
 
 
 

   比较直观的想法是使用括号将代码段括起来 
 
 
 

     
 
 
 

   #define MIN(a,b) \ 
 
 
 

   {        \ 
  
   typeof (a) _a = (a); \ 
  
   typeof (b) _b = (b); \ 
  
       (_a < _b) ? _a : _b; \ 
  
 

   } 
 
 
 

     
 
 
 

   但这样的定义,当我们按照常规这样来使用的话 
 
 
 

     
 
 
 

   z=MIN(x++,y++); 
 
 
 

     
 
 
 

   展开后变成了z={typeof (x++) _x = (x++); typeof (y++) _y = (y++); (_a < _b) ? _a : _b;}; 
 
 
 

     
 
 
 

   结尾的";"还是会导致编译错误 
 
 
 

     
 
 
 

   解决这个问题的比较理想的做法是: 
 
 
 

     
 
 
 

   #define MIN(a,b) \ 
 
 
 

   do {        \ 
  
   typeof (a) _a = (a); \ 
  
   typeof (b) _b = (b); \ 
  
       (_a < _b) ? _a : _b; \ 
  
 

   } while (0) 
 
 
 

     
 
 
 

   这里有三点需要注意: 
 
 
 

     
 
 
 

   (i)为了防止参数的多次取值,我们利用临时变量_a, _b来存储了a,b的取值结果,这样就避免了多次取值 
 
 
 

   (ii)由于宏定义只执行字符替换,为了为临时变量声明一个合适的类型,因此使用了类型声明, typeof 
 
 
 

         typeof的介绍参见文档  
  http://gcc.gnu.org/onlinedocs/gcc/Typeof.html 
 
 
 

   (iii)为了防止宏使用结尾的分号导致错误,尽量使用do while(0)来讲代码块包含起来 
 
 
 

     
 
 
 

   看来,写好一个宏真是不容易。 
 
 
 

     
 
 
 

   p.s.在看LVS源码时,能看到很多这种定义,这下总算一释心中疑惑了。 
 
 
 

     
 
 
 

   #define IP_VS_BUG() BUG() 
  
 #define IP_VS_ERR_RL(msg, ...)      \ 
  
  do {        \ 
  
   if (net_ratelimit())     \ 
  
    pr_err(msg, ##__VA_ARGS__);   \ 
  
  } while (0) 
  
 

   #ifdef CONFIG_IP_VS_DEBUG 
  
 #define EnterFunction(level)      \ 
  
  do {        \ 
  
   if (level <= ip_vs_get_debug_level())   \ 
  
    printk(KERN_DEBUG    \ 
  
           pr_fmt("Enter: %s, %s line %i\n"), \ 
  
           __func__, __FILE__, __LINE__);  \ 
  
  } while (0) 
  
 #define LeaveFunction(level)      \ 
  
  do {        \ 
  
   if (level <= ip_vs_get_debug_level())   \ 
  
    printk(KERN_DEBUG    \ 
  
           pr_fmt("Leave: %s, %s line %i\n"), \ 
  
           __func__, __FILE__, __LINE__);  \ 
  
  } while (0) 
  
 #else 
  
 #define EnterFunction(level)   do {} while (0) 
  
 #define LeaveFunction(level)   do {} while (0) 
  
 #endif 
  
 

   #define IP_VS_WAIT_WHILE(expr) while (expr) { cpu_relax(); }