我們知道,如果有一個結構體定義如下:
- struct _st {
- int a;
- char b;
- } st ;
我們可以通過st訪問到a或者b,方法就是st.a或者如果有st的指針pst,那麼就用pst->a)。但是,如果知道了結構體中元素的指針,是否可以獲得當前結構體的指針呢?或者說,如果我只能訪問到b,我可以訪問到st和a麼?
首先,這樣做有什麼用呢?其實,自己早就知道linux內核中linus就是使用了container_of()的宏,就是利用的這個方法。最近,在項目中,碰到了一個問題,想了想暫時沒有想到特別好的方法。而使用這個方法倒是可以比較好的解決問題。
大致上問題抽象出來就是:有一些BLOCK,需要用多個鏈表串起來。比如有B1,B2,B3,B4,B5共5個BLOCK,要用三個不同功能的鏈表串起來:
鏈表1: B1->B4->B5
鏈表2: B2->B5
鏈表3: B1->B2->B3->B4->B5
而項目希望使用glib中的鏈表庫實現。而glib中的鏈表庫這裡舉一個單向鏈表的例子)是這樣組織的參見Glib文檔):
- typedef struct {
- gpointer data;
- GSList *next;
- } GSList;
數據用data指針鏈接起來,實現單向鏈表。這裡,如果有人有推薦的庫也希望說出來,呵呵。這裡我們如果用glib的庫顯然不容易實現我們想要的功能,因為我們不知道B2的next是要指向B5如鏈表2中)還是B3如鏈表3中)。我們顯然需要多個next指針做到這一點。而如果我們這樣使用:
- struct _block {
- int data;
- GSlist list1;
- GSlist list2;
- GSlist list3;
- };
因為每一個GSlist的next指針都是指向下一個block的list1或者list2/3)元素的,我們也難以得到data字段的數據。這時候,就需要通過list1的指針找到data。而這也就是我們提出的問題。
大致想一下可以知道,如果我們可以知道結構體在內存中存在的方式。比如:
這樣,我們就可以通過加減法計算出pa所指向的這個整形a所屬於的結構體的地址。而以上的所有,編譯器是肯定知道的,但是我們又怎麼在代碼中體現呢?
其實,為了用代碼表示出來,我們根本不用知道上面的問題。看下面的方法一段便知道了。
既然之前知道在linux內核中,linus使用了container_of()這樣tricky的宏,來通過一個結構體中元素的指針獲得當前結構體的指針,這裡,直接拿過來用其實就好了。
container_of()的實現方法很簡單,也很巧妙。其核心就是兩個宏定義:
- #ifndef offsetof
- #define offsetof(type, field) ((long) &((type *)0)->field)
- #endif /* offsetof */
- #ifndef container_of
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
這裡主要有三個宏:typeof(), offsetof(), container_of()。我談談我的一些理解:
- int main(void)
- {
- printf("%d.\n", (1,2,3));
- printf("%d.\n", ({int i=1,j;j=i+2;}));
- return 0;
- }
這裡我舉一個簡單的使用container_of()的例子:
- /*
- * desc : a simple example to use container_of() macro
- * mail : [email protected]
- */
- #include <stdio.h>
- #ifndef offsetof
- #define offsetof(type, field) ((long) &((type *)0)->field)
- #endif /* offsetof */
- #ifndef container_of
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
- typedef struct _A {
- int a;
- char b;
- long long c;
- double d;
- } SA ;
- SA sa = {
- .a = 10,
- .b = 'c',
- .c = 204,
- .d = 3.14,
- };
- int main(int argc,char *argv[])
- {
- double *pd = &sa.d;
- /* now we have a pointer pd -> sa.d, let's access element c with pd */
- printf("SA.c = %lld\n", container_of(pd, SA, d)->c);
- return 0;
- }
這裡,我定義了一個結構sa,並通過sa.d的指針得到sa.c的值。輸出是:
- SA.c = 204
這樣,我們自然也可以通過block.next得到block.data了。
本文出自 “LoudMouth Peter” 博客,請務必保留此出處http://xzpeter.blog.51cto.com/783279/339497