在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的地址。