container_of 宏、offsetof 宏 分析

container_of 是Linux中常用的宏,其作用就是根据结构体成员变量的地址获取结构体的地址。

container_of 在include/linux/kernel.h 中定义:

 /**
   * container_of - cast a member of a structure out to the containing structure
   * @ptr:        the pointer to the member.
   * @type:       the type of the container struct this is embedded in.
   * @member:     the name of the member within the struct.
   *
   */
  #define container_of(ptr, type, member) ({ \
          const typeof(((type *)0)->member) * __mptr =(ptr); \
          (type *)((char *)__mptr - offsetof(type, member)); })

这么一个宏定义,可以说是把C语言的指针运用的出神入化。这个宏可以分三步来解读。
第一步:定义了一个与 ptr 相同类型的指针 __mptr,这个指针类型通过 typeof(((type *)0)->member) 来获得,然后将__mptr 赋值为 ptr。
typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛,其作用就是通过一个变量获取它的类型。
要获取结构体中member成员的类型,首先将零地址强制转换为结构体类型指针( (type *)0 ),再通过这种指针得到这个结构体的 member 成员变量((type*)->member),然后通过这个变量得到它的类型(typeof(((type *)0)->member) )。

第二部:用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址。
member成员在结构体中的偏移地址通过 offsetof 宏得到:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这个宏首先将零地址强制转换为结构体类型指针((type )0),再用这个指针得到MEMBER成员(((type *0)->MEMBER)),然后获取这个成员的地址(&(&((TYPE )0)->MEMBER)),最后将这个地址强转为size_t类型。因为这时候结构体首地址为0,所以成员变量的地址就是在结构体中断地址偏移。

第三步:用大括号将两条语句括起来,作为一个整体的语句块,在这个语句块外面在包一层小括号,使这个语句块的值可以被外部使用。
通过分析这个宏我才发现,一个语句块也是有返回值的,还能被外部使用,例如以下语句也是可以的:

int a = ({ int b = 2; int c = 3; b + c;});

总感觉为了实现container_of这个宏真是大费周章啊,用的着那么麻烦吗?自己写的话一行代码搞定:

#define container_of(ptr, type, member) (type *)((char *)ptr - offsetof(type, member))

乍一看似乎没毛病,仔细研究还是没毛病,但Linux内核中的定义方法自然有它的道理。当传入的指针ptr类型和结构中member成员类型不一样时,内核中的定义方法编译时会有一个指针类型不匹配的警告,而自己的定义方法悄无声息的编译通过了。不得不说,Linux开发人员考虑的真周到。

流云非晚

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值