/* 代碼如下 */
#define rt_container_of(ptr, type, member)\
( (type*)( (char*)(ptr) - (unsigned long)( &((type*)0)->member ) ) )
下面來(lái)分析一下:
通過(guò)將0強(qiáng)制轉(zhuǎn)化為指向type類型的指針后歪脏,對(duì)其進(jìn)行->操作够坐,我們就可以獲得在0地址開(kāi)始定義的type結(jié)構(gòu)體內(nèi)某個(gè)成員的地址划栓,接下來(lái)將該地址轉(zhuǎn)化為offset,offset偏移是一個(gè)數(shù)星爪,又因?yàn)槭?2機(jī)器疆栏,所以用(unsigned long)
曾掂,并且offset每加一,就代表多偏移一個(gè)字節(jié)壁顶,分析到這我們解釋完了(unsigned long)(&((type*)0)->member)
遭殉,
那么(char*)(ptr)
是用來(lái)干嘛的呢,為什么不直接ptr - (unsigned long)(&((type*)0)->member)
呢博助;
回想一下指針和數(shù)的運(yùn)算险污,指針減去一個(gè)數(shù)后變成什么地址是要看指針的類型的,假設(shè)一個(gè)指向16位整型的指針減去1富岳,那么地址會(huì)減2蛔糯,因?yàn)?6位占用兩個(gè)字節(jié),結(jié)合上面所分析的offset中的1代表的是一個(gè)字節(jié)的偏移窖式,那么什么類型的指針加減1代表一個(gè)字節(jié)的偏移呢蚁飒?當(dāng)然是(char*)
!
最后使用(type*)
將偏移后的(char*)
轉(zhuǎn)化為指向type類型的指針萝喘,就大功告成了淮逻。
可能的疑問(wèn):
1. 某些書(shū)不是講指針操作很危險(xiǎn),為什么可以直接將0轉(zhuǎn)化為(type*)指針案篝ぁ爬早?
首先要明白指針操作危險(xiǎn)在哪,指針解引用用作左值是很危險(xiǎn)的启妹,因?yàn)橐坏┑刂峰e(cuò)了筛严,
你就把原來(lái)的正常值給篡改了,但是看看上面代碼饶米,里面沒(méi)有解引用(*poniter)桨啃,
也就是說(shuō),搞了這么多花里胡哨的都是假把式檬输,并沒(méi)有真正的對(duì)指針?biāo)傅拇鎯?chǔ)空間
進(jìn)行操作照瘾,如此一來(lái),談何危險(xiǎn)丧慈。
2. (unsigned long)的用途
解釋過(guò)了
3. (char*)的用途
也解釋過(guò)了
4. 非得將0轉(zhuǎn)化為(type*)嗎析命,其它數(shù)可不可以?
當(dāng)然可以,但是0最方便碳却,其它數(shù)你不得再做一次減法队秩,如下
&( ((type *)3264)->member ) - 3264
// linux下如何實(shí)現(xiàn)?
/*
* 選自 linux-2.6.7 內(nèi)核源碼
* filename: linux-2.6.7/include/linux/stddef.h
*/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/*
* 選自 linux-2.6.7 內(nèi)核源碼
* filename: linux-2.6.7/include/linux/stddef.h
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/*
* 需要注意,這里采用的宏定義是({})格式昼浦,這種格式可以直接用作賦值表達(dá)式右值
* 但是取的值是宏定義中最后一句的值
* 至于為什么要定義一個(gè)__mptr馍资,我猜可能是防止ptr所指內(nèi)容被意外更改吧
*/