宏 list_entry 用来通过结构体的成员指针来返回整个结构体的地址。
一、定义
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
二 、解析
预备知识,需要解析这个宏我们需要有以下预备知识:
首先来分析 offsetof 宏。它的作用是用于计算 TYPE 结构体中成员 MEMBER 的偏移量。
测试程序如下:
#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) );})
typedef struct
{
char char1;
int value1;
float value2;
}my_struct;
int offsetof_test(void)
{
my_struct test;
int len;
printf("struct len = %d\r\n", sizeof(my_struct));
len = offsetof(my_struct, char1);
printf("value1 offset = %d\r\n",len);
len = offsetof(my_struct, value1);
printf("value1 offset = %d\r\n",len);
len = offsetof(my_struct, value2);
printf("value1 offset = %d\r\n",len);
}
int main(int argc, char *argv[])
{
offsetof_test();
while (1);
}
/*
struct len = 12
value1 offset = 0
value1 offset = 4
value1 offset = 8
*/
typeof。typeof 是 GNU C 编译器的特有关键字(C 语言中是看不到的),注意 typeof 只在编译期生效,用于得到变量的类型。
测试程序如下
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 100;
typeof(i) j;// 这里就相当于 int j;
j = i;
printf("i = %d, j = %d\r\n", i, j);
while (1);
}
/*
i = 100, j = 100
*/
({ })。({ }) 是 GNU C 的语法扩展(C 语言中也是看不到的),它的用法和逗号表达式类似,结果为最后一个语句的值
测试程序如下
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
int j = 0;
int a = 0;
int b = 0;
i = (a = 1, b = 2, a + b);
j = ({a = 1; b = 2; a + b;});
printf("i = %d, j = %d\r\n", i, j);
while (1);
}
/*
i = 3, j = 3
*/
最后我们来分析 container_of 这个宏。通过前面的分析我们可以直接看最后一个语句
(type *)( (char *)__mptr - offsetof(type,member) )
这里面有一个指针 __mptr ,它在第二行中定义,类型由 typeof 来获得。指针 __pmtr 和指针 ptr 的值是一样的。而 ptr 又是宏 container_of 的一个参数,它是指向 type 结构体中成员 member 的一个指针,所以 __pmtr 也指向 type 结构体成员 member 。
( (char *)__mptr - offsetof(type,member) )
最终就实现了通过结构体成员找到结构体的起始地址。__mptr 前面的 char * 是为了进行指针运算的,以实现逐字节相减。
测试程序如下
#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 test_struct
{
int num1;
int num2;
float fl;
};
int test_containerof(void)
{
struct test_struct init_test_struct = { 99, 18, 59.12 };
int *char_ptr = &init_test_struct.num1;
struct test_struct *test_struct = container_of(char_ptr, struct test_struct, num1);
printf("init test struct address is %p\r\n", &init_test_struct);
printf("get test struct address is %p\r\n", test_struct);
if (&init_test_struct != test_struct)
{
printf("get struct address failed\r\n");
}
else
{
printf("get struct address successful\r\n");
}
printf("test_struct->num1 = %d\ntest_struct->num2 = %d\ntest_struct->fl = %f\n",
test_struct->num1, test_struct->num2, test_struct->fl);
return 0;
}
int main(int argc, char *argv[])
{
test_containerof();
while (1);
}
/*
init test struct address is 0028FF08
get test struct address is 0028FF08
get struct address successful
test_struct->num1 = 99
test_struct->num2 = 18
test_struct->fl = 59.119999
*/