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到底是如果實現的?








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章