list_entry

在RT_Thread 中有这样一个宏定义:

#define rt_list_entry(node, type, member) \
    ((type *)((char *)(node) - (unsigned long)(&((type *)0)->member)))

其最终返回的是type结构体的地址。
在Linux内核中,获取节点地址的函数list_entry()非常常用,如下:

/**
* list_entry - get the struct for this entry
* @ptr:        the &struct list_head pointer.
* @type:        the type of the struct this is embedded in.
* @member:        the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) /
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

显然rt_list_entry 是参考了linux中的list_entry:从一个结构的成员指针找到其容器的指针。

(char )(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type )0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针。

这么做的目的在于,在Object容器中,有很多个列表的存在,例如设备列表,信号列表,事件列表等等,如果对每个列表都建立一个链表,会出现由于数据类型不同而导致大量冗余代码。

如果我们的程序要将一组int变量和一组char变量组织成链表。那么对于int链表的表示方式为:

struct list_int
{
int data_t;
struct list_int *next;
}

char的表示方式为

struct list_char
{
char data_t;
struct list_char *next;
}

那么每个list的操作,比如创建,删除,插入函数,都得写2个,分别针对于int和char。因为C语言不能根据输入参数的类型实现不同的调用。

为了避免这种情况,可以采用如下方式来完成泛型,首先定义一个链表结构:

struct list
{
struct list *next;
}

然后在数据对象中包含这个链表对象

struct obj_int
{
    list node;
    int data_t;
}

然后编写创建,删除,插入函数,这些方法函数是针对struct list的而个跟data_t的类型无关,然后每个Node都相当于是obj_int对象的首地址,通过node地址就得到了obj_int的地址。

参考文章:
每天了解RTT多一点(1)——rt_device_find函数浅析
list_entry()详解

猜你喜欢

转载自blog.csdn.net/u011559046/article/details/80304463