正文
大家周末好,我是bug菌~ 今天主要是跟大家詳細(xì)聊聊container_of這個(gè)宏定義,非常經(jīng)典的宏,只是一直沒(méi)有抽時(shí)間細(xì)細(xì)品味,今天就跟大家一起來(lái)看看有何神奇之處:
1offsetof
首先我們需要簡(jiǎn)單看看offsetof(TYPE, MEMBER) 這個(gè)宏定義,它是用于計(jì)算一個(gè)結(jié)構(gòu)體中某個(gè)成員的偏移量。
其第一個(gè)參數(shù) TYPE 是一個(gè)結(jié)構(gòu)體類(lèi)型,第二個(gè)參數(shù) MEMBER 是 TYPE 中的一個(gè)成員變量名。
它將返回類(lèi)型為 size_t 的整數(shù),表示 MEMBER 相對(duì)于 TYPE 起始地址的偏移量。
基本原理是根據(jù) C 語(yǔ)言的數(shù)據(jù)對(duì)齊機(jī)制,成員變量在類(lèi)型定義中的相對(duì)位置決定了它的偏移量。
#defineoffsetof(TYPE,MEMBER)((size_t)&((TYPE*)0)->MEMBER)
該宏定義使用了C語(yǔ)言中的指針運(yùn)算和類(lèi)型轉(zhuǎn)換。具體實(shí)現(xiàn)步驟如下:
1、(TYPE *)0:將0強(qiáng)制類(lèi)型轉(zhuǎn)換為指向類(lèi)型為T(mén)YPE的指針,得到了一個(gè)結(jié)構(gòu)體TYPE的空指針。
2、&((TYPE *)0)->MEMBER:求出結(jié)構(gòu)體類(lèi)型TYPE中成員MEMBER的地址。其巧妙之處在于,由于空指針不指向任何對(duì)象,因此這個(gè)成員的地址就是相對(duì)于結(jié)構(gòu)體首地址的偏移量。
3、(size_t):將偏移量轉(zhuǎn)換為無(wú)符號(hào)整型數(shù),以滿足C語(yǔ)言標(biāo)準(zhǔn)庫(kù)中對(duì)offsetof()返回值的類(lèi)型要求。
該宏定義可以在編譯時(shí)就直接計(jì)算出偏移量,避免了運(yùn)行時(shí)的計(jì)算開(kāi)銷(xiāo),因此比通過(guò)變量名訪問(wèn)成員的方式更為高效,通常用在需要直接訪問(wèn)結(jié)構(gòu)體成員的底層代碼中,例如在操作系統(tǒng)內(nèi)核、嵌入式系統(tǒng)以及一些高性能計(jì)算應(yīng)用中。
structTestStruct{ intvalue1; charvalue2; doublevalue3; }; size_toffset=offsetof(structTestStruct,value2);
如上例,offset 變量將會(huì)存儲(chǔ) value2 相對(duì)于 TestStruct 起始地址的偏移量。在這種情況下,因?yàn)?TestStruct 中的 value1 通常占用了 4 個(gè)字節(jié),value2 占用了 1 個(gè)字節(jié),所以 value2 相對(duì)于結(jié)構(gòu)體起始地址的偏移量應(yīng)該是 4。
2container_of
講完offsetof,來(lái)到今天的主角container_of,container_of()是一個(gè)在linux內(nèi)核中經(jīng)常使用的宏,用于獲取一個(gè)結(jié)構(gòu)體成員指針所在它所屬的結(jié)構(gòu)體的指針,有點(diǎn)繞口,細(xì)細(xì)品味。
該宏包括也主要包括三個(gè)參數(shù):
ptr:結(jié)構(gòu)體中某個(gè)成員的指針;
type:結(jié)構(gòu)體類(lèi)型名稱(chēng);
member:結(jié)構(gòu)體中ptr指向的成員名稱(chēng)。
首先,宏container_of()確定了ptr指向的成員在結(jié)構(gòu)體中的偏移(offset)。通過(guò)offsetof()宏就可以得到這個(gè)偏移,其參數(shù)為結(jié)構(gòu)體類(lèi)型和成員名稱(chēng)。得到偏移后,再通過(guò)減去偏移的方式得到指向整個(gè)結(jié)構(gòu)體的指針,巧妙吧。
具體實(shí)現(xiàn)如下:
#definecontainer_of(ptr,type,member)({ consttypeof(((type*)0)->member)*__mptr=(ptr); (type*)((char*)__mptr-offsetof(type,member));})
其中,typeof是GCC的一個(gè)擴(kuò)展關(guān)鍵字,用于返回一個(gè)表達(dá)式的類(lèi)型,可惜,大部分非GCC編譯器不一定能支持。
假設(shè)ptr指向的成員變量的類(lèi)型為T(mén),__mptr就是一個(gè)指向T類(lèi)型的指針。然后,調(diào)用offsetof()即可得到member在type類(lèi)型中的偏移量,最后返回一個(gè)指向type類(lèi)型的指針。
注意,尖括號(hào)不能省略,因?yàn)樗硎绢?lèi)型轉(zhuǎn)換。此外,container_of()宏使用了一個(gè)GCC的語(yǔ)言擴(kuò)展"statement expression",即后面的{},可以在其中包含多條語(yǔ)句。
下面給出一個(gè)示例,用于說(shuō)明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成員,通過(guò)container_of()宏獲得了指向整個(gè)struct student結(jié)構(gòu)體的指針pstu,然后就可以訪問(wèn)id和name成員了。
審核編輯:湯梓紅
-
C語(yǔ)言
+關(guān)注
關(guān)注
183文章
7634瀏覽量
143942 -
結(jié)構(gòu)體
+關(guān)注
關(guān)注
1文章
131瀏覽量
11248 -
宏定義
+關(guān)注
關(guān)注
0文章
51瀏覽量
9336
原文標(biāo)題:搞懂它,就可以把結(jié)構(gòu)體玩活了~
文章出處:【微信號(hào):最后一個(gè)bug,微信公眾號(hào):最后一個(gè)bug】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
C語(yǔ)言宏定義使用技巧
Linux內(nèi)核中container_of原理詳解

這個(gè)奇怪的宏定義要干啥???
揭開(kāi)linux內(nèi)核中container_of的神秘面紗
offsetof宏與container_of宏詳解
宏定義問(wèn)題!
C語(yǔ)言宏定義使用技巧
內(nèi)聯(lián)函數(shù)和宏定義的區(qū)別介紹

不帶參數(shù)的宏定義是什么?不帶參數(shù)的宏定義的資料介紹詳細(xì)過(guò)程概述
使用AVR單片機(jī)編寫(xiě)的宏定義的應(yīng)用代碼詳細(xì)資料免費(fèi)下載

Linux內(nèi)核中的宏/container_of分析

c語(yǔ)言帶參數(shù)的宏定義
container_of()宏,太妙了~

評(píng)論