首页 > 技术文章 > 有意思的C宏

pianist 2013-08-28 00:52 原文

在Linux内核嵌入式代码等传统的C代码里,会有一些难以识别的宏定义我记得在eCos, UBoot, FFmpeg有一些比较BT的宏定义,很难读懂。对于C++程序员来说,最好将这种难读的宏定义转成inline函数或模板函数本章对这些较难的重定义进行汇总。

1. ; 在宏定义中指定义类型参数
   1: #define FPOS_TO_VAR(fpos, typed, var)                    (var) = (typed)((fpos).__pos)
   2: #define VAR_TO_FPOS(fpos, var)                           (fpos).__pos = (var)

此句在faac的代码里可以见到,其特殊之处是以类型名作为参数,而且是用宏定义一行赋值语句像如下的调用:

  

   1: uint64_t  ret;
   2: FPOS_TO_VAR(fpos,  uint64_t, ret);
   3:  
   4: uint64_t ret;
   5: ret = (uint64_t) (fpos.__pos);
   6:  
一般可以对宏定义作换行显示,换行后会更清晰易懂一些:

   1: #define FPOS_TO_VAR(fpos, typed, var)     \   
   2:                    (var) = (typed)((fpos).__pos)
2 . 宏定义取结构偏移

这种用法似乎在uCosII中用到,记不清了。举例来说,我们知道一个struct(类型为T)中有一个变量为v,那么由v的地址来得到T的指针呢?

#define S_OFFSET(T,v)      (&((T*)0)->v)

   1: struct ABC
   2: {
   3:     int a;
   4:     char b;
   5:     int c;
   6: };
   7:  
   8: int main()
   9: {
  10:     unsigned int offset = (unsigned int) S_OFFSET(ABC, b);
  11:     return 0;
  12: }

主要是分析一下(&((T*)0)->v)是个什么意思,相当于以下几种代码:
  1. T* p = (T*) 0;  // 把0地址强转为一个T*  
  2. void* offset = &p->v; // 由于变量p的地址为0,所以成员变量v的偏移就是其地址 
T* p = (T*) 0;  // 把0地址强转为一个T*
void* offset = &p->v; // 由于变量p的地址为0,所以成员变量v的偏移就是其地址
得到上述偏移之后怎么用呢?在函数传递的过程中,只需要一个v的指针,就可以反推出其所在的结构的地址而offset一般不方便指定为一个固定值,因为不同环境下的padding可能不一致,也不方便扩展。
  1. void* p = ...;  
  2. T* t = (T*) (p - offset); 
void* p = ...;
T* t = (T*) (p - offset);
3 . 宏定义中的连接符##

这也是一种比较特殊的用法,在eCos3中经常用到用于替换一个完整名字(类型名、函数变量名) 的一部分。以下再种写法等效,请仔细体会:

my_type_t   var1;
my_type_t   var2;

 

   1: struct my_type_t
   2: {
   3:         int a;
   4:         char b;
   5: };
   6:  
   7: #define    MY_TYPE(T,  name)       \
   8:         T  var##name
   9:  
  10: MY_TYPE(my_type_t, 1);
  11: MY_TYPE(my_type_t, 2);
  12:  
  13: int main()
  14: {
  15:         unsigned int offset = (unsigned int) S_OFFSET(my_type_t, b);
  16:         var1.a = 10;
  17: }

4 . 宏定义中的字符串#
   1: define  MYSTR(value)   #value
   2: int main()
   3: {
   4:         char* cc = MYSTR(hello);
   5: }
相当于

char* cc = "hello";

友情链接:C/C++宏的奇巧淫技

推荐阅读