天天看點

linux核心container_of宏定義分析

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

1. ( (TYPE *)0 ) 将零轉型為TYPE類型指針;

2. ((TYPE *)0)->MEMBER 通路結構中的資料成員;

3. &( ( (TYPE *)0 )->MEMBER )取出資料成員的位址;

4.(size_t)(&(((TYPE*)0)->MEMBER))結果轉換類型;

巧妙之處在于将0轉換成(TYPE*),結構以記憶體空間首位址0作為起始位址,則成員位址自然為偏移位址。

舉例說明:

#include<stdio.h>
typedef struct _test
{
      char i;
      int j;
      char k;
}Test;
int main()
{
      Test *p = 0;
      printf("%p\n", &(p->k));
}
           

答案:00000008

自己分析:這裡使用的是一個利用編譯器技術的小技巧,即先求得結構成員變量在結構體中的相對于結構體的首位址的偏移位址,然後根據結構體的首位址為0,進而得出該偏移位址就是該結構體變量在該結構體中的偏移,即:該結構體成員變量距離結構體首的距離。在offsetof()中,這個member成員的位址實際上就是type資料結構中member成員相對于結構變量的偏移量。對于給定一個結構,offsetof(type,member)是一個常量,list_entry()正是利用這個不變的偏移量來求得連結清單資料項的變量位址。

二、container_of()

container_of() 來自\linux\kernel.h

核心中的注釋:container_of - cast a member of a tructure 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 truct.

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

自己分析:

1.(type *)0->member為設計一個type類型的結構體,起始位址為0,編譯器将結構體的起始的位址加上此結構體成員變量的偏移得到此結構體成員變量的偏移位址,由于結構體起始位址為0,是以此結構體成員變量的偏移位址就等于其成員變量在結構體内的距離結構體開始部分的偏移量。即:&(type *)0->member就是取出其成員變量的偏移位址。而其等于其在結構體内的偏移量:即為:(size_t)(& ((type *)0)->member)經過size_t的強制類型轉換後,其數值為結構體内的偏移量。該偏移量這裡由offsetof()求出。

2.typeof( ( (type *)0)->member )為取出member成員的變量類型。用其定義__mptr指針.ptr為指向該成員變量的指針。__mptr為member資料類型的常量指針,其指向ptr所指向的變量處。

3.(char *)__mptr轉換為位元組型指針。(char *)__mptr - offsetof(type,member))用來求出結構體起始位址(為char *型指針),然後(type *)( (char *)__mptr -offsetof(type,member) )在(type *)作用下進行将位元組型的結構體起始指針轉換為type *型的結構體起始指針。

4.({ })這個擴充傳回程式塊中最後一個表達式的值。

      這就是從結構體某成員變量指針來求出該結構體的首指針。指針類型從結構體某成員變量類型轉換為該結構體類型。

三、typeof關鍵字

typeof關鍵字是C語言中的一個新擴充。

它的作用是來擷取變量的資料類型

typeof的參數可以是兩種形式:表達式或類型

用類型作參數的例子:

   typeof(int *) a,b; //擷取了int *的類型并用此類型定義了變量a和b

等價于:

   int *a,*b;

下面是兩個等效聲明,用于聲明int類型的變量a。

   typeof(int) a;

   typeof('b') a;

使用表達式的的例子:

extern int foo();

   typeof(foo()) var;

聲明了int類型的var變量,因為表達式foo()是int類型的。由于表達式不會被執行,是以不會調用foo函數。

一般情況下用typeof就可以了,但是如果要于ISO C相容的話,最好是用雙下劃線的形式:__typeof__。

typeof和typedef很像,事實上,隻要能用typedef的地方就可以用typeof。

下面是另外一些例子:

把y定義成x指向的資料類型:

   typeof(*x) y;

把y定義成x指向資料類型的數組:

   typeof(*x) y[4];

把y定義成一個字元指針數組:

   typeof(typeof(char *)[4] y;

這與下面的定義等價:

   char *y[4];

我們再換一種定義方式:

   #define pointer(T) typeof(T *)

   #define array(T,N) typeof(T [N])

   array (pointer(char),4) y;

如果想把T定義成一個表達式的類型,則我們僅僅用typedef無法做到

但可以通過typeof做到:

   typdef typeof(expr) T;

使用typeof的聲明示例

以下示例用于聲明指針和數組。為了進行對比,還給出了不帶typeof的等效聲明。

   typeof(int *) p1,p2;

   int *p1, *p2;

   typeof(int) *p3,p4;

   int *p3, p4;

   typeof(int [10]) a1, a2;

   int a1[10], a2[10];

使用typeof的聲明限制

請注意,typeof構造中的類型名不能包含存儲類說明符,如extern或static。不過允許包含類型限定符,如const或volatile。

例如,下列代碼是無效的,因為它在typeof構造中聲明了extern:

   typeof(extern int) a;

下列代碼使用外部連結來聲明辨別符b是有效的,表示一個int類型的對象。下一個聲明也是有效的,它聲明了一個使用const限定符的char類型指針,表示指針p不能被修改。

   extern typeof(int) b;

   typeof(char * const) p = "a";

在宏聲明中使用typeof

typeof構造的主要應用是用在宏定義中。可以使用typeof關鍵字來引用宏參數的類型。是以,在沒有将類型名明确指定為宏實參的情況下,構造帶有所需類型的對象是可能的。

下面是一個交換兩個變量的值的宏定義:

   #define SWAP(a,b) {\

      typeof(a) _t=a;\

      a=b;\

      b=_t;}

這個宏可以交換所有基本資料類型的變量(整數,字元,結構等)

參考:

http://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof