正文
大家周末好,我是bug菌~ 今天主要是跟大家詳細聊聊container_of這個宏定義,非常經典的宏,只是一直沒有抽時間細細品味,今天就跟大家一起來看看有何神奇之處:
1offsetof
首先我們需要簡單看看offsetof(TYPE, MEMBER) 這個宏定義,它是用于計算一個結構體中某個成員的偏移量。
其第一個參數 TYPE 是一個結構體類型,第二個參數 MEMBER 是 TYPE 中的一個成員變量名。
它將返回類型為 size_t 的整數,表示 MEMBER 相對于 TYPE 起始地址的偏移量。
基本原理是根據 C 語言的數據對齊機制,成員變量在類型定義中的相對位置決定了它的偏移量。
#defineoffsetof(TYPE,MEMBER)((size_t)&((TYPE*)0)->MEMBER)
該宏定義使用了C語言中的指針運算和類型轉換。具體實現步驟如下:
1、(TYPE *)0:將0強制類型轉換為指向類型為TYPE的指針,得到了一個結構體TYPE的空指針。
2、&((TYPE *)0)->MEMBER:求出結構體類型TYPE中成員MEMBER的地址。其巧妙之處在于,由于空指針不指向任何對象,因此這個成員的地址就是相對于結構體首地址的偏移量。
3、(size_t):將偏移量轉換為無符號整型數,以滿足C語言標準庫中對offsetof()返回值的類型要求。
該宏定義可以在編譯時就直接計算出偏移量,避免了運行時的計算開銷,因此比通過變量名訪問成員的方式更為高效,通常用在需要直接訪問結構體成員的底層代碼中,例如在操作系統內核、嵌入式系統以及一些高性能計算應用中。
structTestStruct{ intvalue1; charvalue2; doublevalue3; }; size_toffset=offsetof(structTestStruct,value2);
如上例,offset 變量將會存儲 value2 相對于 TestStruct 起始地址的偏移量。在這種情況下,因為 TestStruct 中的 value1 通常占用了 4 個字節,value2 占用了 1 個字節,所以 value2 相對于結構體起始地址的偏移量應該是 4。
2container_of
講完offsetof,來到今天的主角container_of,container_of()是一個在linux內核中經常使用的宏,用于獲取一個結構體成員指針所在它所屬的結構體的指針,有點繞口,細細品味。
該宏包括也主要包括三個參數:
ptr:結構體中某個成員的指針;
type:結構體類型名稱;
member:結構體中ptr指向的成員名稱。
首先,宏container_of()確定了ptr指向的成員在結構體中的偏移(offset)。通過offsetof()宏就可以得到這個偏移,其參數為結構體類型和成員名稱。得到偏移后,再通過減去偏移的方式得到指向整個結構體的指針,巧妙吧。
具體實現如下:
#definecontainer_of(ptr,type,member)({ consttypeof(((type*)0)->member)*__mptr=(ptr); (type*)((char*)__mptr-offsetof(type,member));})
其中,typeof是GCC的一個擴展關鍵字,用于返回一個表達式的類型,可惜,大部分非GCC編譯器不一定能支持。
假設ptr指向的成員變量的類型為T,__mptr就是一個指向T類型的指針。然后,調用offsetof()即可得到member在type類型中的偏移量,最后返回一個指向type類型的指針。
注意,尖括號不能省略,因為它表示類型轉換。此外,container_of()宏使用了一個GCC的語言擴展"statement expression",即后面的{},可以在其中包含多條語句。
下面給出一個示例,用于說明container_of()的使用方法:
#include#include #definecontainer_of(ptr,type,member)({ consttypeof(((type*)0)->member)*__mptr=(ptr); (type*)((char*)__mptr-offsetof(type,member));}) structstudent{ intid; charname[20]; }; intmain(){ structstudentstu={10001,"ZhangSan"}; char*pname=stu.name; structstudent*pstu=container_of(pname,structstudent,name); printf("ID:%d,Name:%s ",pstu->id,pstu->name); return0; }
如上例,pname指向stu的name成員,通過container_of()宏獲得了指向整個struct student結構體的指針pstu,然后就可以訪問id和name成員了。
審核編輯:湯梓紅
-
C語言
+關注
關注
180文章
7604瀏覽量
136692 -
結構體
+關注
關注
1文章
130瀏覽量
10840 -
宏定義
+關注
關注
0文章
50瀏覽量
9005
原文標題:搞懂它,就可以把結構體玩活了~
文章出處:【微信號:最后一個bug,微信公眾號:最后一個bug】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論