程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> [c]如何通過結構體元素找到結構體?

[c]如何通過結構體元素找到結構體?

編輯:關於C語言

1. 問題提出

我們知道,如果有一個結構體定義如下:

  1. struct _st { 
  2.   int a; 
  3.   char b; 
  4. } st ; 

我們可以通過st訪問到a或者b,方法就是st.a或者如果有st的指針pst,那麼就用pst->a)。但是,如果知道了結構體中元素的指針,是否可以獲得當前結構體的指針呢?或者說,如果我只能訪問到b,我可以訪問到st和a麼?

2. 這個問題的實際意義

首先,這樣做有什麼用呢?其實,自己早就知道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文檔):

  1. typedef struct { 
  2.   gpointer data; 
  3.   GSList *next; 
  4. } GSList;

數據用data指針鏈接起來,實現單向鏈表。這裡,如果有人有推薦的庫也希望說出來,呵呵。這裡我們如果用glib的庫顯然不容易實現我們想要的功能,因為我們不知道B2的next是要指向B5如鏈表2中)還是B3如鏈表3中)。我們顯然需要多個next指針做到這一點。而如果我們這樣使用:

  1. struct _block { 
  2.   int data; 
  3.   GSlist list1; 
  4.   GSlist list2; 
  5.   GSlist list3; 
  6. }; 

因為每一個GSlist的next指針都是指向下一個block的list1或者list2/3)元素的,我們也難以得到data字段的數據。這時候,就需要通過list1的指針找到data。而這也就是我們提出的問題。

3. 考慮

大致想一下可以知道,如果我們可以知道結構體在內存中存在的方式。比如:

這樣,我們就可以通過加減法計算出pa所指向的這個整形a所屬於的結構體的地址。而以上的所有,編譯器是肯定知道的,但是我們又怎麼在代碼中體現呢?

其實,為了用代碼表示出來,我們根本不用知道上面的問題。看下面的方法一段便知道了。

4. container_of()的分析

 既然之前知道在linux內核中,linus使用了container_of()這樣tricky的宏,來通過一個結構體中元素的指針獲得當前結構體的指針,這裡,直接拿過來用其實就好了。

container_of()的實現方法很簡單,也很巧妙。其核心就是兩個宏定義:

  1. #ifndef offsetof 
  2. #define offsetof(type, field)   ((long) &((type *)0)->field) 
  3. #endif   /* offsetof */ 
  4.  
  5. #ifndef container_of 
  6. #define container_of(ptr, type, member) ({          \ 
  7.     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \ 
  8.     (type *)( (char *)__mptr - offsetof(type,member) );}) 
  9. #endif 

這裡主要有三個宏:typeof(), offsetof(), container_of()。我談談我的一些理解:

  1. int main(void) 
  2.     printf("%d.\n", (1,2,3)); 
  3.     printf("%d.\n", ({int i=1,j;j=i+2;})); 
  4.     return 0; 

5. container_of()的使用例子

這裡我舉一個簡單的使用container_of()的例子:

  1. /*
  2. * desc : a simple example to use container_of() macro
  3. * mail : [email protected]
  4. */
  5. #include <stdio.h> 
  6.  
  7. #ifndef offsetof 
  8. #define offsetof(type, field)   ((long) &((type *)0)->field) 
  9. #endif   /* offsetof */ 
  10.  
  11. #ifndef container_of 
  12. #define container_of(ptr, type, member) ({          \ 
  13.     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \ 
  14.     (type *)( (char *)__mptr - offsetof(type,member) );}) 
  15. #endif 
  16.  
  17. typedef struct _A { 
  18.     int a; 
  19.     char b; 
  20.     long long c; 
  21.     double d; 
  22. } SA ; 
  23.  
  24. SA sa = { 
  25.     .a = 10, 
  26.     .b = 'c', 
  27.     .c = 204, 
  28.     .d = 3.14, 
  29. }; 
  30.  
  31. int main(int argc,char *argv[]) 
  32.     double *pd = &sa.d; 
  33.     /* now we have a pointer pd -> sa.d, let's access element c with pd */ 
  34.     printf("SA.c = %lld\n", container_of(pd, SA, d)->c); 
  35.     return 0; 

這裡,我定義了一個結構sa,並通過sa.d的指針得到sa.c的值。輸出是:

  1. SA.c = 204 

這樣,我們自然也可以通過block.next得到block.data了。

參考資料:

本文出自 “LoudMouth Peter” 博客,請務必保留此出處http://xzpeter.blog.51cto.com/783279/339497

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved