我對CONTAINING RECORD巨集的詳細解釋

2021-06-19 07:07:16 字數 2467 閱讀 5892

巨集containing_record的用處其實還是相當大的, 而且很是方便, 它的主要作用是:

根據結構體中的某成員的指標來推算出該結構體的指標!

下面從乙個簡單的例子開始說起:

我們定義乙個結構體, 同時型別化:

typedef struct

ss;

這是乙個很簡單的結構體, 沒什麼特殊的, 稍微分析下該結構體:

結構體的大小(位元組):4+4+4=12位元組

成員a的偏移:0

成員b的偏移:4

成員c的偏移:8

我們用ss來定義乙個變數:

ss s = ;

那麼此時a,b,c的值分別為:a=1,b=2,c=3.

其實編譯器在生成**的時候其實是這樣給成員變數賦值的:

假定s的位址為:0x12000000, 則:

*(int*)((char*)&s + 0) = 1;

*(int*)((char*)&s + 4) = 2;

*(int*)((char*)&s + 8) = 3;

也就是說是在&s的位址基礎上加上變數的偏移來確定成員的指標並賦值的, 所以:

&s->a 將得到 0x12000000 + 0 = 0x12000000

&s->b 將得到 0x12000000 + 4 = 0x12000004

&s->c 將得到 0x12000000 + 8 = 0x12000008

所以:    結構體的位址   +  成員變數的偏移 = 

成員變數的位址

移一下項:  成員變數的位址 -  成員變數的偏移 = 

結構體的位址

哇哇, 這就是我們想要的位址, 不就是做了個減法嘛~~~囧

首先, 成員變數的位址是我們知道的. 

其次, 我們需要得到成員變數的偏移(假定為成員b的偏移).

怎麼辦呢? 我們可以這樣:

&s->b - (unsigned long)&s, 這樣就可以得到成員b的偏移了, 但是, 但是, &s 是我們需要的, 顯然暫時是個未知數, 既然這樣...

那, 我們再做一次減法吧(非正確的c語言表示式, 不過結果沒問題, 這裡只是顯得清楚點):

&(s-s)->b - (unsinged long)&(s-s), 

其中, 為保證型別一致, 需要:

s-s = (ss*)0

(unsigned long)&(s-s) = (unsigned long)(ss*)0 = 0, 直接省略該部分就可以了

那麼, 化簡得到: &((ss*)0)->b - (unsigned long)0

最簡結果: &((ss*)0)->b, 這就是b的偏移

哈哈, 很簡單吧, 0指標的妙用, 總共做了兩次減法而已~ 對你們數學帝來說肯定不是問題啦~

其中, 我們需要知道ss結構體的原型, ss結構體中的某個成員變數b(其實無論哪個都一樣, 只是要和前面提供指標的那個變數要一致)

總結下, 我們需要提供:結構體中某個成員變數的位址, 該結構體的原型, 該結構體中的某個成員變數(與前面要是同乙個變數)

最終的containing_record的定義為:

#define containing_record(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))

addr: 結構體中某個成員變數的位址

type: 結構體的原型

field: 結構體的某個成員(與前面相同)

好了, 所有的結論都出來了, 這是乙個萬能公式, 不管成員變數是哪乙個結果都正確, 這是相對於知道第乙個變數的位址而言的:

如果知道的是第乙個成員的位址(pa = &s->a)的話, 這是最簡單的情況了:

直接強制型別轉換就可以了: (ss*)pa 即可, 此時 &((type*)0)->field 這部分恰好為0

所以結果直接就是((type*)addr)了, 最簡單的情況. 也是我們最容易想到的一種情況, 比如把鍊錶元素放在結構體的最開始 ~~~

到這裡這個containing_record巨集就已經說完了~  

現在, 我們在使用list_entry等雙向鍊錶時, 不管把該鍊錶放在結構體的哪個地方, 都可以在遍歷鍊錶時通過containing_record巨集來準確得到整個結構體的位址了~

記得移除鍊錶中的某個元素的時候, 要free整個結構體的位址才行哦, wdk提供的操作函式只是把該鍊錶元素脫離整個鍊錶~~~

btw:

把addr轉換為 unsigned char*的原因是在指標計算時的計算單位為1, 也就是說 (unsigned char*)addr+1 = addr+1, 不轉換的話肯定是錯誤的

把&((type*)0)->field轉換為(unsigned long)4個位元組寬的同時是要保證表示式不是由兩個指標的算術操作構成的, 因為c語言標準未定義那樣的運算

寫了這麼多, 希望沒落下什麼吧~

注:該文章**自:

我對SNS我理解

模仿也要做得比真的還好,比如qq,它的業務包括客戶端都是模仿別人的,但能做得像它那樣,的確不容易。sns在國內肯定能火的,不過現在的sns 只是太跟風了,沒有了自己的特點,很多的sns 很難看出它到底還是不是sns,或者它到底去哪個方向?自己都沒有弄清楚。所以sns在國內是很有機遇的。其實在當前經濟...

我對我的思考

時間不經意的就過去了四天了,從窗外看著日出日落,聽著水木年華憂傷的情歌,吃著舍友為我們打回來的飯。啊!大學的生活原來還有這麼美好,生命總是給你出其不意的一頁。可是,總有一些好事者卻喊著 放我們出去 等等的口號,我不明白他 她 們究竟 want to do?這也許就是人與人之間至少是在思想的差異吧!就...

我對BroadcastReceiver的簡單理解

1.在androidmanifest.xml中先註冊寫好的mybroadcastreceive 2.寫乙個類來extends類broadcastreceiver,並實現onreceiver方法 package org.lzm.android.broadcastreceiver import andr...