Linux container_of宏詳細(xì)剖析
掃描二維碼
隨時隨地手機(jī)看文章
1 offsetof宏的原理以及作用
在使用container_of宏之前,我們先來了解下offsetof這個宏,它在Linux內(nèi)核里的源碼是這個樣子:
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
1.1 offsetof宏的工作原理
虛擬一個TYPE
類型的結(jié)構(gòu)體變量,通過TYPE.MEMBER
的方式來訪問MEMBER
成員,進(jìn)而得到MEMBER
成員相對于整個結(jié)構(gòu)體首地址的偏移量。
這句話理解起來看似很抽象,&((TYPE *)0)->MEMBER
相當(dāng)于得到了成員的偏移減去0地址偏移,也就是結(jié)構(gòu)體的首地址,進(jìn)而就得到了該成員相當(dāng)于整個結(jié)構(gòu)體的偏移量,接下來寫一個例子就明白了:
1.2 offsetof宏編程案例
#include <stdio.h>
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
#pragma pack(4)
struct ptr
{
char a ;
short b ;
int c ;
double d ;
};
#pragma pack()
int main(void)
{
struct ptr Pt ;
printf("ptr:%d\n",sizeof(struct ptr));
//相對地址偏移量
int offset = offsetof(struct ptr,a);
printf("offset:%d\n",offset);
int offset1 = offsetof(struct ptr,b);
printf("offset1:%d\n",offset1);
int offset2 = offsetof(struct ptr,c);
printf("offset2:%d\n",offset2);
int offset3 = offsetof(struct ptr,d);
printf("offset3:%d\n",offset3);
return 0 ;
}
運(yùn)行結(jié)果:
在案例中,我們以默認(rèn)4字節(jié)對齊得到的4個結(jié)構(gòu)體變量在結(jié)構(gòu)體中的偏移,明白了offsetof
宏如何使用,就解決了我們的大疑問了,我們來看看container_of
宏怎么使用吧。
2 Linux container_of宏的原理以及作用
Linux中的container_of
宏長如下這個樣子,那它有什么作用呢?我們來詳細(xì)剖析一下。
#define container_of(ptr, type , member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr) ; \
(type *)((char *)__mptr - offsetof(type,member)) ;})
2.1 container_of宏的工作原理
先用typeof
獲取變量的數(shù)據(jù)類型,也就是member
成員的類型,然后將member
這個成員 的指針轉(zhuǎn)成自己類型的指針,再從offsetof相減,就得到整個結(jié)構(gòu)體變量的首地址了,再將該地址強(qiáng)制轉(zhuǎn)化為type *
。
接下來寫一個關(guān)于container_of
宏的編程案例:
2.2 container_of宏編程案例
#include <stdio.h>
#include <stdlib.h>
//獲取結(jié)構(gòu)體成員相對于結(jié)構(gòu)體的偏移
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
//通過獲取結(jié)構(gòu)體中的某個成員的,反推該結(jié)構(gòu)體的指針
#define container_of(ptr, type , member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr) ; \
(type *)((char *)__mptr - offsetof(type,member)) ;})
//讓結(jié)構(gòu)體默認(rèn)以四字節(jié)對齊
#pragma pack(4)
struct ptr
{
char a ;
short b ;
int c ;
double d ;
};
#pragma pack()
int main(void)
{
struct ptr Pt ;
struct ptr *pt ;
printf("ptr:%d\n",sizeof(struct ptr));//16
//獲取結(jié)構(gòu)體的首地址
printf("ptr:%p\n",&Pt); //0028FEA8
Pt.a = 'a';
Pt.b = 2 ;
Pt.c = 4 ;
Pt.d = 12.04 ;
//通過container_of獲取結(jié)構(gòu)體的首地址
pt = container_of(&Pt.c, struct ptr , c);
printf("pt:%p\n",pt);
printf("a:%c\n",pt->a) ;
printf("b:%d\n",pt->b) ;
printf("c:%d\n",pt->c) ;
printf("d:%.2lf\n",pt->d);
return 0 ;
}
運(yùn)行結(jié)果:
往期精彩
談?wù)勛霎a(chǎn)品、做項目以及標(biāo)準(zhǔn)化相關(guān)的話題
推薦一個非常好的項目管理工具
帶串口屏顯示的Bootloader
分享一個很好用的按鍵組件
若覺得本次分享的文章對您有幫助,隨手點[在看]
并轉(zhuǎn)發(fā)分享,也是對我的支持。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!