【數據結構與算法】 Linux內核中雙向鏈表的實現基礎

在前面文章鏈表(請戳我)中總結了雙向鏈表,咱們繼續對Linux內核中雙向鏈表進行探討,將分爲兩篇文章講述,本文則主要涉及Linux內核中很是經常使用的兩個經典宏定義offsetof和container_of。它是理解Linux內涵雙向鏈表的基礎。linux

假若你查看過Linux Kernel的源碼,那麼你對 offsetof 和 container_of 這兩個宏應該不陌生。這兩個宏最初是極客寫出的,後來在Linux內核中被推廣使用。下面分別介紹。算法

offsetof

定義:offsetof在linux內核的include/linux/stddef.h中定義。服務器

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

功能:得到結構體(TYPE)的變量成員(MEMBER)在此結構體中的偏移量。
【數據結構與算法】 Linux內核中雙向鏈表的實現基礎
(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

定義: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)"來獲取指向整個結構體變量的指針。
【數據結構與算法】 Linux內核中雙向鏈表的實現基礎
(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

專一服務器後臺技術棧知識總結分享

歡迎關注交流共同進步

【數據結構與算法】 Linux內核中雙向鏈表的實現基礎

碼農有道 coding

碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!

相關文章
相關標籤/搜索