linux kernel typeof container_of

最近发现两个非常牛的宏:

一个是计算某个结构体成员的偏移量,

一个是计算一个结构体的首地址

这两个宏在Linux kernel里非常基础,非常常用,

今天抽闲暇时间好好调试了一番。

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
 * 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)); })


其中,typeof的使用类似于sizeof的使用,

typeof返回一个数据类型,sizeof返回一个数据结构的大小(byte),


gcc version

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)


1、宏offsetof的例子(来自man offsetof)

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
   struct s {
  int i;
  char c;
  double d;
  char a[];
   };

   /* Output is compiler dependent */
   printf("offsets: i=%ld; c=%ld; d=%ld a=%ld\n",
  (long) offsetof(struct s, i),
  (long) offsetof(struct s, c),
  (long) offsetof(struct s, d),
  (long) offsetof(struct s, a));
   printf("sizeof(struct s)=%ld\n", (long) sizeof(struct s));

   exit(EXIT_SUCCESS);
}

//output

offsets: i=0; c=4; d=8 a=16
sizeof(struct s)=16

【分析】

  • 该宏中字段TYPE为结构体类型,MEMBER为结构体内的变量名。
  • (TYPE *)0) 是欺骗编译器说有一个指向结构TYPE 的指针,其地址值0 。
  • (TYPE *)0)->MEMBER 是获得结构体TYPE中成员变量MEMBER的地址. 因为基址为0,所以,这时MEMBER的地址当然就是MEMBER在TYPE中的偏移了。
  • 同时也考虑了字节对齐。


2、宏container_of的例子

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#define container_of(ptr, type, member) ({            \
 const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
 (type *)( (char *)__mptr - offsetof(type,member) );})

int main(void)
{
   struct s {
  int i;
  char c;
  double d;
  char a[];
   };
   
   struct s m_s, *p_m_s;
   double  *p_d = &m_s.d;
   printf("0x%.8x\n",&m_s);
   printf(" 0x%.8x \n 0x%.8x \n 0x%.8x \n 0x%.8x \n",&m_s.i,&m_s.c,&m_s.d,m_s.a);
   //p_m_s = container_of(p_d, struct s, d);
   printf("--0x%x\n",container_of(p_d, struct s, d));
   exit(EXIT_SUCCESS);
}

output

0xb39e8290
 0xb39e8290 
 0xb39e8294 
 0xb39e8298 
 0xb39e82a0 
--0xb39e8290

其中有三个地址是一样的,也就是结构体的首地址。

【分析】

  • typeof( ( (type *)0)->member )为取出member成员的变量类型,
  • 定义__mptr指针ptr为指向该成员变量的指针(即指向ptr所指向的变量处),
  • (char *)__mptr - offsetof(type,member)), 用该成员变量的实际地址减去该变量在结构体中的偏移,来求出结构体起始地址,
  • 在这个例子里如果不添加宏container_of,有可能编译不过,原因:或许typeof是库自带,或许是我的编译路径不全,或许其它^_^。




另外,最近老在想sizeof到底是如果实现的?








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值