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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

container_of 和 offsetof 宏详解

發布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 container_of 和 offsetof 宏详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在linux內核鏈表中,會遇到兩個宏。

在include/linux/stddef.h中,有這樣的定義

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

這里的TYPE表示某個結構體類型,MEMBER表示結構體中的一個成員,這個宏求出了成員在結構體中的位置偏移(以字節為單位)

如果你還不理解,我們舉個例子吧。

struct student {char name[20];unsigned char age; };int main(void) {int off = offsetof(struct student, age);printf("off = %d\n",off); } 運行結果是off = 20


其實宏里面的0只是一種特殊情況而已。對這個宏的解釋是:假設結構體處于0x1234這個地址,

((TYPE*)0x1234 )-> MEMBER
->比類型轉換的優先級高,所以要加括號。上面就得到了成員,再取地址,得到成員的地址

&((TYPE*)0x1234 )-> MEMBER

不用加括號,因為&的優先級比較低

再來一個類型轉換

(size_t)&((TYPE*)0x1234 )-> MEMBER

終于得到成員的起始地址了,還沒有完,既然算偏移,就要減去結構體的起始地址

(size_t)&((TYPE*)0x1234 )-> MEMBER ?- 0x1234

不難看出,這里的0x1234換成什么數字都可以,因為偏移是和起始地址無關的。

不信的話可以把這個宏定義改一改,再測試一下

#define offsetof(TYPE, MEMBER) ( (size_t) &((TYPE *)0x2222)->MEMBER - 0x2222 )struct student {char name[20];unsigned char age; };int main(void) {int off = offsetof(struct student, age);printf("off = %d\n",off); } 改成0x2222后,結果還是20.

既然什么數字都可以,那就改成0吧,于是后面的-0就可以省略了。于是就得到了開頭的那個宏。


下面我們說另外一個宏。

/**

827? * container_of - cast a member of a structure out to the containing structure

828? * @ptr:? ? the pointer to the member.

829? * @type: ? the type of the container struct this is embedded in.

830? * @member: the name of the member within the struct.

831? *

832? */

833 #define container_of(ptr, type, member) ({? ? ? ? ? \

834 ? ? const typeof( ((type *)0)->member ) *__mptr = (ptr);? ? \

835 ? ? (type *)( (char *)__mptr - offsetof(type,member) );})


這個宏就是從一個成員的地址得到這個結構體的地址,俗稱小指針轉大指針。

繼續舉例子。

struct student {char name[20];unsigned char age; };int main(void) {struct student stu = {"wangdong",22};printf("&stu = %p\n",&stu);printf("&stu.age = %p\n",&stu.age);struct student *p = container_of(&stu.age, struct student, age);printf("p= %p\n",p); }運行結果為

&stu = 0x7fff53df9c40

&stu.age = 0x7fff53df9c54

p= 0x7fff53df9c40

第一行和第三行的值是一致的。
如果你不理解這個宏的定義,我們先簡化一下它,這樣寫 #define container_of(ptr, type, member) ? ? ? ? ? ? ( ?(type *)( (char *)ptr - offsetof(type,member) ) ?)
(char *)ptr 成員的地址
offsetof(type,member) ?成員的偏移
二者相減,就是結構體的起始地址,最后再加個強制類型轉換。 可是為什么不是這樣寫的呢,而是要多出來一行

const typeof( ((type *)0)->member ) *__mptr = (ptr);

沒有這行到底行不行,其實也行,用上面的例子,去掉這行,也可以得到一樣的結果。

先看看這行什么意思吧,?((type *)0)->member 這是成員,typeof( ((type *)0)->member ) 得到了成員的類型,假設就是unsigned char類型,

const typeof( ((type *)0)->member ) *__mptr 定義了一個這個類型的指針

const unsigned char * __mptr = (ptr); 賦值給定義的這個指針。

我苦思冥想,又結合網上的資料,認為這樣寫是做了一個類型檢查。如果有了這行,假設結構體就沒有member這個成員,那么編譯會報錯。

所以這樣寫保證了member確實是type的一個成員。

還有,這樣寫也保證了ptr確實是這個成員類型的指針,如果不是,編譯也會報錯。再做個實驗。

把剛才的代碼改一下

struct student *p = container_of((int *)&stu.age, struct student, age);

故意把&stu.age轉換成int*,編譯就會報警告:

test.c:33:22: warning: incompatible pointer types initializing 'const typeof (((struct student *)0)->age)

? ? ? *' (aka 'const unsigned char *') with an expression of type 'int *' [-Wincompatible-pointer-types]

? ? ? ? struct student *p = container_of((int *)&stu.age, struct student, age);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

test.c:11:39: note:expanded from macro 'container_of'

? ? ? ? const typeof( ((type *)0)->member ) *__mptr = (ptr);? ? \

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ^? ? ? ? ~~~~~


如果沒有這一行,那么就不會報錯。

所以,作者的出發意圖是千方百計地讓程序員寫出安全的代碼啊!真的是用心良苦。

(完)

總結

以上是生活随笔為你收集整理的container_of 和 offsetof 宏详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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