淺析linux中的宏contianer_of

    container_of是linux中的一個宏,它的做用是經過結構體中某一成員的地址來得到該成員所在結構體的地址。其定義在include/linux/kernel.h中653行,以下所示:linux

    下面對上述代碼進行簡單的介紹:對於第653行,該行定義了宏名,而且該宏包含三個參數:ptr用於存放結構體中某已知成員的地址;type用於指明該結構體的名稱;member用於存放ptr所對應的成員在結構體中的名稱。對於第654行,它的本質是定義了一個變量,並對其賦值。其中typeofgccc語言的擴展保留字,用於得到變量的類型。此時該語句就不難理解:這裏首先將強制轉化爲該結構體類型的指針,而後經過該指針指向相應的成員變量,接着再經過typeof獲取該成員變量的類型。因此這行代碼的意思就是定義一個與結構體成員變量member同類型的指針變量,並將ptr賦值給它。對於第655行,因爲ptr存放的是結構體中某成員的地址,因此_mptr存放的也就是該成員的地址;offsetof用於得到結構體成員member相對於結構體起始地址的偏移量,因此該結構體的地址就應該爲該成員變量的地址減去其相對於結構體起始地址的偏移量。數組

    宏offsetof用於得到結構體中某一成員相對於結構體起始地址的偏移量。它定義在include/linux/stddef.h中第24行:spa

其中(TYPE *)0 是騙編譯器說有一個指向該結構體TYPE的指針,其值爲&((TYPE *)0)->MEMBER用於得到結構體中成員變量MEMBER的地址,因爲該結構體類型的變量的基地址爲,此時MEMBER的地址就是其相對於結構體的偏移量;而後再將結果強制轉化爲size_t,其在標準c庫中定義爲unsigned int指針

    下面舉例進行說明該宏的用法(因爲該宏是在內核態定義的,用戶態沒法使用,這裏將該宏的定義拷貝到用戶態進行使用)編譯器

程序一:io

#include <stdio.h>編譯

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

#define container_of(ptr,type,member)({ \變量

const typeof(((type *)0)->member)* _mptr=(ptr); \gcc

(type *)((char *)_mptr-offsetof(type,member));})

struct student{

char *num;

char *name;

int age;

};

int main()

{

struct student stu={"0001","zhang",10};

struct student *s=container_of(&(stu.name),struct student,name);

printf("name=%s,sex=%s,age=%d\n",s->num,s->name,s->age);

return 0;

}

程序二:

#include <stdio.h>

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

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

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

(type *)((char *)_mptr-offsetof(type,member));})

struct student{

char num[5];

char name[10];

int age;

};

int main()

{

struct student stu={"0001","zhang",10};

struct student *s=container_of(stu.name,struct student,name);

printf("num=%s,name=%s,age=%d\n",s->num,s->name,s->age);

return 0;

}

    對上述程序分別進行編譯,其結果以下圖所示,其中圖一爲程序一的編譯結果;圖二爲程序二的編譯結果。

圖一 程序一的編譯結果

圖二 程序二的編譯結果

分析:爲何程序二的編譯會出現警告呢(上述警告的意思爲指針賦值時類型不兼容)?緣由很簡單,下面分別將該宏展開以下所示:

程序一:

#define offsetof(struct student,name) ((size_t)&((struct student *)0)->name)

#define container_of(&(stu.name),struct student,name)({ \

const typeof(((struct student *)0)->name)* _mptr=(&(stu.name)); \

(struct student *)((char *)_mptr-offsetof(struct student,name));})

程序二:

#define offsetof(struct student,name) ((size_t)&((struct student *)0)->name)

#define container_of(stu.name,struct student,name)({ \

const typeof(((struct student *)0)->name)* _mptr=(stu.name); \

(struct student *)((char *)_mptr-offsetof(struct student,name));})

    對於程序段一中的第三行,這段代碼的意思是定義一個char類型的二級指針_mptr。由於student struct結構體中name成員爲char類型的一級指針,經過typeof後便獲得其類型,而後再定義一個指向字符指針類型的指針_mptr;接着將stu.name的地址賦值給_mptr。因爲stu.name爲字符指針類型,取地址後即爲二級字符指針。綜上知,編譯時不會出現警告信息。

    對於程序段二中的第三行,這段代碼的意思也是定義一個char類型的二級指針_mptr。其中大部分都和程序段一中的相同,但在賦值時,因爲stu.name爲指向一個長度爲10的字符數組的指針常量,因此類型不匹配,所以在編譯時會產生警告信息。在該程序中,若是將container_of宏的第一個參數設置爲&(stu.name),在編譯時一樣會出現上述警告。此時只需將該宏的第二行中的const關鍵字刪除便可,刪除後編譯將不會出現警告。對於發生這種錯誤的緣由如今我還不清楚,有待於之後的探究。

相關文章
相關標籤/搜索