众所周知,linux内核完全使用C语言进行构筑,但是却大量使用了面向对象的思想。C语言实现面向对象时,可以利用结构体的特性实现封装、继承,同时也可以使用函数指针的方式模拟出多态。这里会出现一个问题,如以下People和Student的例子,使用结构体实现继承操作实际上使用子结构体包含父结构体对象来实现的。在这种方式下子类对象使用父类的成员很容易,但是反过来想用父类获取子类的成员就很困难了,因为父类是被包裹的对象。
此时,内核使用了container_of和offsetof这两个牛掰的宏来解决了这个难题。
typedef struct __People{
int age;
char* name;
}People
typedef struct __Student{
int ID;
People people;
}
offsetof
/**
定义位置:include/linux/stddef.h
作用:获取TYPE类型结构体成员MEMBER相对该结构体首地址的偏移
**/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
一步一步拆解:
(1)传入两个参数,TYPE表示结构体的类型,MEMBER是结构体内的某个成员
(2)(TYPE *)0,初看很懵逼,其实就是将0强制转换为TYPE类型的指针 PS:这里巧妙地利用了0来直接获取相对偏移地址,因为0的基地址就是0,且仅在编译时使用合法。
(3)&((TYPE *)0)->MEMBER,获取转换为TYPE类型的0的成员MEMBER,然后取其地址,该地址就是MEMBER相对结构体首地址的偏移地址
(4)外层的(size_t)很容易理解,就是将类型转换为无符号整型。
offsetof宏可以获取MEMBER相对于结构体的地址偏移量,这有什么用呢?有了相对偏移量,不就可以获取结构体首地址了吗,那不就是从成员地址反向获取了结构体的地址,即实现了父类获取子类成员的能力。
container_of
/**
定义位置:include/linux/kernel.h
作用:由结构体成员获取结构体首地址
**/