作者:邹飞邹飞邹飞 | 来源:互联网 | 2023-09-25 17:07
在阅读源码的过程中,我发现很多的代码中都采用了链表,链表的也是非常有意义的一种。有我们在C语言中使用的那种数据嵌套指针的方式。也有在linux中将链表作为一个单独的对象,然后将这个对象嵌入到不同的对象中,然后根据container_of()得到对应的对象指针。这些方式都是常用的方式之一。
在看uC/OS-II中我阅读源码时发现其中竟然很少有关于链表的操作。开始也没有仔细的去分析原因,我甚至认为位图的方式取代了链表。因为uC/OS-II基本上可以任务是基于数组等静态内存分布的方式,全局变量的形式可以通过位图简单的链接在一起。
但是在阅读事件标志组的过程中我发现其中还是存在很多关于链表的操作的,比如很多的如何将事件标志节点链接起来,但是分析源码并没有
typedef struct { /* Event Flag Wait List Node */
void *OSFlagNodeNext; /* Pointer to next NODE in wait list */
void *OSFlagNodePrev; /* Pointer to previous NODE in wait list */
void *OSFlagNodeTCB; /* Pointer to TCB of waiting task */
void *OSFlagNodeFlagGrp; /* Pointer to Event Flag Group */
OS_FLAGS OSFlagNodeFlags; /* Event flag to wait on */
INT8U OSFlagNodeWaitType; /* Type of wait: */
/* OS_FLAG_WAIT_AND */
/* OS_FLAG_WAIT_ALL */
/* OS_FLAG_WAIT_OR */
/* OS_FLAG_WAIT_ANY */
} OS_FLAG_NODE;
#endif
从上面的代码可以发现并没有使用OS_FLAG_NODE的指针形式,而是采用了void *的指针形式,结合具体的实现过程我发现这样的定义方式确实相比我们之前传统的定义方式存在很多的优点,首先这种连接方式比传统的链接方式更加的灵活多变,并一定指向的内容就是自己定义的这种结构体,因为void *这种全能的指针形式扩大了对不同类型的链接能力,使得链表的优势更加的明显。
pnode->OSFlagNodeNext = pgrp->OSFlagWaitList; /* Add node at beginning of event flag wait list */
pnode->OSFlagNodePrev = (void *)0;
pnode->OSFlagNodeFlagGrp = (void *)pgrp; /* Link to Event Flag Group */
pnode_next = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;
if (pnode_next != (void *)0) { /* Is this the first NODE to insert? */
pnode_next->OSFlagNodePrev = pnode; /* No, link in doubly linked list */
}
pgrp->OSFlagWaitList = (void *)pnode;
上面是我从源码中复制出来的部分代码其中就有这种链表的操作方式,可以发现这种void*的类型扩大了链接对象的范围。但同样需要注意的时,在编写代码的过程中需要强制类型转换,也就是链接到链表中时需要转换为void *类型,而当弹出链表以后又需要转换成数据本身的结构类型,这可能导致一些问题的产生。但是void *类型的指针确实能够实现不同对象之间的链接关系。这就类似于在linux中的嵌入式链表非常的类似。
强制类型转换是在使用void *时特别注意的事项。