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