在看Raw OS的源代码时,在raw_list.h里面有如下宏定义:
#define raw_list_entry(node, type, member) ((type *)((RAW_U8 *)(node) - (RAW_U32)(&((type *)0)->member)))
乍一看这个宏定义有点复杂,其实只要按部就班的分析,是很好理解的。我们看调用此宏定义时传进来的形参是什么,全局搜索此宏定义,发现在
void tick_list_update(void)
{
...
RAW_TASK_OBJ *p_tcb;
LIST *iter;
...
p_tcb = raw_list_entry(iter, RAW_TASK_OBJ, tick_list);
...
...
}
函数中调用了该宏定义,其中RAW_TASK_OBJ结构体定义如下:
typedef struct
{
RAW_VOID *task_stack;
#if (CONFIG_RAW_MPU_ENABLE > 0)
RAW_MPU_SETTINGS mpu_settings;
#endif
LIST task_list;
...
...
} RAW_TASK_OBJ;
下面我们开始分析该宏定义的功能:
首先我们分析使用该宏定义时的形参和返回值,我们发现返回值赋值给了p_tcb,而p_tcb是RAW_TASK_OBJ类型的指针,因此我们可以判断该宏定义返回的是RAW_TASK_OBJ类型结构体的地址。
分析形参,第一个形参是iter,类型是LIST循环链表;第二个形参是RAW_TASK_OBJ,其是结构体类型说明;第三个形参是tick_list,其是RAW_TASK_OBJ结构体中一个LIST链表的名称。
分析宏定义结构,
#define raw_list_entry(node, type, member) ((type *)((RAW_U8 *)(node) - (RAW_U32)(&((type *)0)->member)))
发现(type *)强制类型转换,即将一个东西强制转换为type指针类型,而type刚好是输入形参RAW_TASK_OBJ,则(type *)后面大括号中的东西应该是一个其他类型的指针。分析大括号中代码:
((RAW_U8 *)(node) - (RAW_U32)(&((type *)0)->member)))
其中node是传入的iter,一个LIST类型的指针,将该指针强制转换成了RAW_U8 类型的指针。然后该指针减去了一坨奇怪的东西后,就运行结束了。我们发现,一个LIST类型的指针,减去一个值后,就变为了RAW_TASK_OBJ类型的指针。
我们看RAW_TASK_OBJ定义,发现其中有LIST task_list。假设(RAW_U8 *)(node)的地址是RAW_TASK_OBJ中的task_list,则task_list减去在RAW_TASK_OBJ中的偏移量,就可以得到task_list所在结构体的首地址。因此,我们可以初步断定(RAW_U8 *)(node)后面减去的一坨就是task_list在RAW_TASK_OBJ结构体中的偏移量。
下面我们来分析后面的一坨是怎么实现的:
(RAW_U32)(&((type *)0)->member))
type*是输入的结构体类型RAW_TASK_OBJ,将地址0强制转换为RAW_TASK_OBJ类型,然后取task_list在该结构体中的地址。因为RAW_TASK_OBJ是强制从地址0开始的,那么task_list所在的地址也就是相对于地址0的绝对地址,刚好等于task_list在RAW_TASK_OBJ中的偏移量。因此,后面这一坨其实就是算结构体中某个变量在结构体中的偏移量的。