在前面文章鏈表(請戳我)中總結了雙向鏈表,咱們繼續對Linux內核中雙向鏈表進行探討,將分爲兩篇文章講述,本文則主要涉及Linux內核中很是經常使用的兩個經典宏定義offsetof和container_of。它是理解Linux內涵雙向鏈表的基礎。linux
假若你查看過Linux Kernel的源碼,那麼你對 offsetof 和 container_of 這兩個宏應該不陌生。這兩個宏最初是極客寫出的,後來在Linux內核中被推廣使用。下面分別介紹。算法
定義:offsetof在linux內核的include/linux/stddef.h中定義。服務器
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
功能:得到結構體(TYPE)的變量成員(MEMBER)在此結構體中的偏移量。
(1) ( (TYPE )0 ): 將零轉型爲TYPE類型指針,即TYPE類型的指針的地址是0
(2) ((TYPE )0)->MEMBER: 訪問結構中的數據成員。
(3) &( ( (TYPE )0 )->MEMBER ): 取出數據成員的地址。因爲TYPE的地址是0,這裏獲取到的地址就是相對MEMBER在TYPE中的偏移。
(4) (size_t)(&(((TYPE)0)->MEMBER)): 結果轉換類型。對於32位系統而言,size_t是unsigned int類型;對於64位系統而言,size_t是unsigned long類型。
TYPE是結構體,它表明"總體";而MEMBER是成員,它是總體中的某一部分。將offsetof看做一個數學問題來看待,問題就至關簡單了:已知'總體'和該總體中'某一個部分',而計算該部分在總體中的偏移。下面看一個例子。數據結構
#include <stdio.h> // 得到結構體(TYPE)的變量成員(MEMBER)在此結構體中的偏移量。 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) struct student { char gender; int id; int age; char name[20]; }; int main() { int gender_offset, id_offset, age_offset, name_offset; gender_offset = offsetof(struct student, gender); id_offset = offsetof(struct student, id); age_offset = offsetof(struct student, age); name_offset = offsetof(struct student, name); printf("gender_offset = %d\n", gender_offset); printf("id_offset = %d\n", id_offset); printf("age_offset = %d\n", age_offset); printf("name_offset = %d\n", name_offset); }
運行結果ide
gender_offset = 0 id_offset = 4 age_offset = 8 name_offset = 12
定義:container_of在linux內核的include/linux/kernel.h中定義。3d
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
說明:根據"結構體(type)變量"中的"域成員變量(member)的指針(ptr)"來獲取指向整個結構體變量的指針。
(1) typeof( ( (type )0)->member ): 取出member成員的變量類型。
(2) const typeof( ((type )0)->member ) mptr = (ptr): 定義變量mptr指針,並將ptr賦值給mptr。通過這一步,mptr爲member數據類型的常量指針,其指向ptr所指向的地址。
(3) (char )mptr: 將mptr轉換爲字節型指針。
(4) offsetof(type,member)) 就是獲取"member成員"在"結構體type"中的位置偏移。
(5) (char )__mptr - offsetof(type,member)): 就是用來獲取"結構體type"的指針的起始地址(爲char 型指針)。
(6) (type )( (char )__mptr - offsetof(type,member) ): 就是將"char 類型的結構體type的指針"轉換爲"type 類型的結構體type的指針"。
type是結構體,它表明"總體";而member是成員,它是總體中的某一部分,並且member的地址是已知的。指針
將offsetof看做一個數學問題來看待,問題就至關簡單了:已知'總體'和該總體中'某一個部分',要根據該部分的地址,計算出總體的地址。下面看一個例子。code
#include <stdio.h> #include <string.h> // 得到結構體(TYPE)的變量成員(MEMBER)在此結構體中的偏移量。 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) // 根據"結構體(type)變量"中的"域成員變量(member)的指針(ptr)" 來獲取指向整個結構體變量的指針 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct student { char gender; int id; int age; char name[20]; }; int main() { struct student stu; struct student *pstu; stu.gender = '1'; stu.id = 9527; stu.age = 24; strcpy(stu.name, "zhouxingxing"); // 根據"id地址" 獲取 "結構體的地址"。 pstu = container_of(&stu.id, struct student, id); // 根據獲取到的結構體student的地址,訪問其它成員 printf("gender= %c\n", pstu->gender); printf("age= %d\n", pstu->age); printf("name= %s\n", pstu->name); }
運行結果:視頻
gender= 1 age= 24 name= zhouxingxing
精心整理 | 歷史乾貨文章目錄
【福利】本身蒐集的網上精品課程視頻分享(上)
【數據結構與算法】 通俗易懂講解 二叉樹遍歷
【數據結構與算法】 通俗易懂講解 二叉搜索樹blog
碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!