我目前正在开发一个ZigBee WSNDemo项目,我仍然坚持使用这部分代码。基本上,我必须在appInitMsgSender
函数中将此宏用于队列目的。
void appInitMsgSender(void)
{
txState = APP_MSG_TX_FREE_STATE;
resetQueue(&appToSendQueue);
resetQueue(&appFreeQueue);
resetQueue(&appSentQueue);
resetQueue(&appDoneQueue);
for (uint8_t i = 0; i < ARRAY_SIZE(appTxBuffers); i++)
{
putQueueElem(&appFreeQueue, &appTxBuffers[i].next);
}
}
以上是应用程序的消息发送初始化函数。以下是用于它的宏。我想知道两者是如何联系的。我的意思是如何理解这段代码的工作原理。
#define DECLARE_QUEUE(queue) QueueDescriptor_t queue = {.head = NULL,}
// Type of queue element
typedef struct _QueueElement_t
{
struct _QueueElement_t *next;
} QueueElement_t;
// Queue descriptor
typedef struct
{
QueueElement_t *head;
} QueueDescriptor_t;
INLINE void resetQueue(QueueDescriptor_t *queue)
{
queue->head = NULL;
}
我真的很困惑这里使用指针。我知道指针的工作原理和理论背后的原理。但在上述背景下,我感到很困惑。
答案 0 :(得分:3)
这里发生的事情是很多抽象。基本上隐藏细节,使代码的主体更具可读性。当然,你需要在头脑中隐含地理解所有潜在的细节,以便你正在阅读的内容有任何意义。
要声明队列,只需:
DECLARE_QUEUE(appFreeQueue);
如果我们扩展宏,这与写作相同:
QueueDescriptor_t appFreeQueue = {.head = NULL,};
如果我们然后展开QueueDescriptor_t
typedef
,它将与写作相同:
struct appFreeQueue
{
QueueElement_t *head = NULL;
};
如果我们扩展QueueElement_t
typedef
,我们可以看到每个元素都指向同一类型的另一个元素:
struct appFreeQueue
{
typedef struct _QueueElement_t
{
struct _QueueElement_t *next = NULL;
} *head;
};
但是,不是每次想要声明队列时都写出来,而是可以使用DECLARE_QUEUE
宏。这将以链表的形式创建一个空队列。链表通常采用每个链接的形式,包含两个东西,一些数据和指向下一个链接的指针。像这样:
Element1 Element2 Element3
[data][*Element2]-> [data][*Element3]-> [data][NULL]
这样做的好处在于,您不必在启动时阻止一段内存,并将其保留为空并且未使用,直到需要它就像使用数组一样。相反,每个链接都可以在需要时动态分配,并指向它在前一个链接的内存中的位置。
INLINE
函数的行为与宏非常相似。无论你在哪里看到resetQueue
,你都应该用你在那里定义的函数替换它。在这种情况下,它将第一个链接的next
指针设置为NULL
,从而导致空队列。
宏和INLINE
功能之间存在细微差别。这个问题的答案中详细描述了这些差异:Inline functions vs Preprocessor macros
答案 1 :(得分:0)
队列使用称为linked list的数据结构实现。 QueueDescriptor_t
包含指向列表中第一个QueueElement_t
的指针。每个QueueElement_t
都包含指向列表中下一个QueueElement_t
的指针。当QueueDescriptor_t
指针为NULL时,列表为空。当QueueElement_t
指针为NULL时,它是列表中的最后一项。链接列表允许您创建项目集合,但项目不必在内存中连续(next
指针链接到集合中的下一个项目)。为了进行比较,数组是 在内存中连续的项的集合(不需要next
指针)。