Linux-宏 list_entry/container_of

宏 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
*/
发布了124 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tyustli/article/details/103908883
今日推荐