日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[c]如何通过结构体元素找到结构体?

發布時間:2025/6/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [c]如何通过结构体元素找到结构体? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 問題提出

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

  • struct?_st?{?
  • ??int?a;?
  • ??char?b;?
  • }?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文檔):

  • 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。而這也就是我們提出的問題。

    3. 考慮

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

  • 1. 是否按照定義順序放置在內存中?
  • 2. 先定義的元素和后定義的元素,哪個地址在高段?
  • 3. 是否在元素間有reserved區域用來align?
  • 這樣,我們就可以通過加減法計算出pa所指向的這個×××a所屬于的結構體的地址。而以上的所有,編譯器是肯定知道的,但是我們又怎么在代碼中體現呢?

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

    4. container_of()的分析

    ?既然之前知道在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()。我談談我的一些理解:

  • 1. typeof()可以得到一個變量的類型,這個是編譯器要支持的。如我們可以這樣寫代碼:{int a; typeof(a) b;}這段代碼等價于:{int a; int b;}
  • 2. offsetof()利用((type *)0)->field方式得到偏移地址,是假設有一個類型為type的結構在內存的0x0000000處,那么這個結構中field的地址的值就是field字段在結構中的偏移地址了!
  • 3. container_of()首先定義了一個type.member同樣類型的指針__mptr,并將ptr賦值給__mptr,這里其實是為了檢驗ptr的類型是否是type.member,增加了安全性(比如,當我隨便傳一個變量的地址ptr給這個宏后,加了校驗的這個container_of()就會在那行報錯,類型不匹配,如果是強制轉換,編譯或許是通過了,但是關鍵時刻一跑估計就是run time error了...);然后用__mptr的地址值減去member在type結構中的偏移地址,得到原始ptr所在結構的結構體地址。
  • 4. 這里還要注意一個括號的使用方法:({...;...;...;}),我沒有對這個的使用在編譯器的手冊中進行查找,但是大致的理解是,在{}中可以按照平時的習慣寫程序,最后({})中可以看作一個值,它的大小是{}中最后一個語句的值。感覺有些類似于(...,...,...)的寫法,但是只有()時不能有運算的語句。這里我寫了一個簡單的例子,輸出是兩個3:
  • int?main(void)?
  • {?
  • ????printf("%d.\n",?(1,2,3));?
  • ????printf("%d.\n",?({int?i=1,j;j=i+2;}));?
  • ????return?0;?
  • }?
  • 5. container_of()的使用例子

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

  • /*
  • * desc : a simple example to use container_of() macro
  • * mail : xzpeter@gmail.com
  • */
  • #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了。

    參考資料:

  • 1. ”對linux內核代碼的一點疑惑:container_of的冗余?“, http://hi.baidu.com/joec3/blog/item/37b0c8900e397487a977a493.html,(其實在開始寫這篇文章的時候,沒有弄清楚為何要專門要用__mptr這個const指針,在這里找到的答案。同時,還有一篇和我這里分析container_of()很像的文章,都是從三個宏的角度。放到參考2里吧,呵呵)
  • 2. “對linux內核中container_of宏的理解“, http://hi.baidu.com/tim_bi/blog/item/fdc3d81358e1f60b5aaf53c6.html
  • 轉載于:https://blog.51cto.com/xzpeter/339497

    總結

    以上是生活随笔為你收集整理的[c]如何通过结构体元素找到结构体?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。