看ambiq的apollo代码觉得这个链表操作比较方便,记录下。
代码思想:
如上图,定义一个全局变量结构体,结构体中为指向链表的头指针和尾指针,这样比较方便的向头插入(利用pHead)、向尾插入(利用pTail),可以方便的模拟一个fifo,当向非头尾节点即中间的某个节点插入新节点时则从头节点开始遍历,找到此节点(pPrev)把这个新节点插入到pPrev的后面。应用场合是做升序排列,删除是同样道理。
//filename : wsf_queue.h
#ifndef WSF_QUEUE_H
#define WSF_QUEUE_H
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Initialize a queue 初始化链表头指针*/
#define WSF_QUEUE_INIT(pQueue) {(pQueue)->pHead = NULL; (pQueue)->pTail = NULL;}
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! Queue structure */
typedef struct
{
void *pHead; /*! head of queue */
void *pTail; /*! tail of queue */
} wsfQueue_t;
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \fn WsfQueueEnq
*
* \brief Enqueue an element to the tail of a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element.
*
* \return None.
*/
/*************************************************************************************************/
void WsfQueueEnq(wsfQueue_t *pQueue, void *pElem);
/*************************************************************************************************/
/*!
* \fn WsfQueueDeq
*
* \brief Dequeue an element from the head of a queue.
*
* \param pQueue Pointer to queue.
*
* \return Pointer to element that has been dequeued or NULL if queue is empty.
*/
/*************************************************************************************************/
void *WsfQueueDeq(wsfQueue_t *pQueue);
/*************************************************************************************************/
/*!
* \fn WsfQueuePush
*
* \brief Push an element to the head of a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element.
*
* \return None.
*/
/*************************************************************************************************/
void WsfQueuePush(wsfQueue_t *pQueue, void *pElem);
/*************************************************************************************************/
/*!
* \fn WsfQueueInsert
*
* \brief Insert an element into a queue. This function is typically used when iterating
* over a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element to be inserted.
* \param pPrev Pointer to previous element in the queue before element to be inserted.
* Note: set pPrev to NULL if pElem is first element in queue.
* \return None.
*/
/*************************************************************************************************/
void WsfQueueInsert(wsfQueue_t *pQueue, void *pElem, void *pPrev);
/*************************************************************************************************/
/*!
* \fn WsfQueueRemove
*
* \brief Remove an element from a queue. This function is typically used when iterating
* over a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element to be removed.
* \param pPrev Pointer to previous element in the queue before element to be removed.
*
* \return None.
*/
/*************************************************************************************************/
void WsfQueueRemove(wsfQueue_t *pQueue, void *pElem, void *pPrev);
/*************************************************************************************************/
/*!
* \fn WsfQueueCount
*
* \brief Count the number of elements in a queue.
*
* \param pQueue Pointer to queue.
*
* \return Number of elements in queue.
*/
/*************************************************************************************************/
uint16_t WsfQueueCount(wsfQueue_t *pQueue);
/*************************************************************************************************/
/*!
* \fn WsfQueueEmpty
*
* \brief Return TRUE if queue is empty.
*
* \param pQueue Pointer to queue.
*
* \return TRUE if queue is empty, FALSE otherwise.
*/
/*************************************************************************************************/
bool_t WsfQueueEmpty(wsfQueue_t *pQueue);
#ifdef __cplusplus
};
#endif
#endif /* WSF_QUEUE_H */
//filename :wsf_queue.c
#include "wsf_types.h"
#include "wsf_queue.h"
#include "wsf_assert.h"
#include "wsf_cs.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/* Get next queue element */
#define WSF_QUEUE_NEXT(p) (((wsfQueueElem_t *)(p))->pNext)
/**************************************************************************************************
Data Types
**************************************************************************************************/
/* Queue element */
typedef struct wsfQueueElem_tag
{
struct wsfQueueElem_tag *pNext; /* pointer to next element */
} wsfQueueElem_t;
/*************************************************************************************************/
/*!
* \fn WsfQueueEnq 向尾部插入pElem节点
*
* \brief Enqueue and element to the tail of a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element.
*
* \return None.
*/
/*************************************************************************************************/
void WsfQueueEnq(wsfQueue_t *pQueue, void *pElem)
{
WSF_CS_INIT(cs);//临界段初始化
WSF_ASSERT(pQueue != NULL);
WSF_ASSERT(pElem != NULL);
/* initialize next pointer */
WSF_QUEUE_NEXT(pElem) = NULL;
/* enter critical section */
WSF_CS_ENTER(cs);//进入临界段
/* if queue empty 如果头指针空,则直接向头部插入*/
if (pQueue->pHead == NULL)
{
pQueue->pHead = pElem;
pQueue->pTail = pElem;
}
/* else enqueue element to the tail of queue
pTail就是尾部元素,所以直接pTail->next指向pElem就插入到尾部
再更新pTail为新的尾部元素pElem*/
else
{
WSF_QUEUE_NEXT(pQueue->pTail) = pElem;
pQueue->pTail = pElem;
}
/* exit critical section */
WSF_CS_EXIT(cs);//退出临界段
}
/*************************************************************************************************/
/*!
* \fn WsfQueueDeq,头部弹出一个节点
*
* \brief Dequeue and element from the head of a queue.
*
* \param pQueue Pointer to queue.
*
* \return Pointer to element that has been dequeued or NULL if queue is empty.
*/
/*************************************************************************************************/
void *WsfQueueDeq(wsfQueue_t *pQueue)
{
wsfQueueElem_t *pElem;
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
pElem = pQueue->pHead;//pHead就是这个链表的头节点,用pElem保存用于返回
/* if queue is not empty */
if (pElem != NULL)
{
/* set head to next element in queue */
pQueue->pHead = WSF_QUEUE_NEXT(pElem);
//pQueue->pHead指向之前pElem的next即跳过头节点指向第二个节点
/* check for empty queue
这种情况是只有一个节点时,弹出则pHead和pTail都要NULL*/
if (pQueue->pHead == NULL)
{
pQueue->pTail = NULL;
}
}
/* exit critical section */
WSF_CS_EXIT(cs);
return pElem;//返回头部弹出节点
}
/*************************************************************************************************/
/*!
* \fn WsfQueuePush 头部插入pElem
*
* \brief Push and element to the head of a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element.
*
* \return None.
*/
/*************************************************************************************************/
void WsfQueuePush(wsfQueue_t *pQueue, void *pElem)
{
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
WSF_ASSERT(pElem != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
/* else push element to head of queue
让pElem->next指向第一个节点即pQueue->pHead*/
WSF_QUEUE_NEXT(pElem) = pQueue->pHead;
/* if queue was empty set tail
这种情况是当原本没有节点,此次插入的是作为链表的一地个节点*/
if (pQueue->pHead == NULL)
{
pQueue->pTail = pElem;
}
/* set head
更新链表的头节点为pElem*/
pQueue->pHead = pElem;
/* exit critical section */
WSF_CS_EXIT(cs);
}
/*************************************************************************************************/
/*!
* \fn WsfQueueInsert,向pPrev节点后插入pElem节点
*
* \brief Insert an element into a queue. This function is typically used when iterating
* over a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element to be inserted.
* \param pPrev Pointer to previous element in the queue before element to be inserted.
* Note: set pPrev to NULL if pElem is first element in queue.
* \return None.
*/
/*************************************************************************************************/
void WsfQueueInsert(wsfQueue_t *pQueue, void *pElem, void *pPrev)
{
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
WSF_ASSERT(pElem != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
/* if queue empty or inserting at tail
没有节点或者pPrev是尾节点直接向尾部插入*/
if (pQueue->pHead == NULL || pPrev == pQueue->pTail)
{
/* queue as normal */
WsfQueueEnq(pQueue, pElem);
}
/* else if inserting at head
如果pPrev == NULL向头部插入*/
else if (pPrev == NULL)
{
/* push to head */
WsfQueuePush(pQueue, pElem);
}
else //pElem插入到pPrev的后面
{
/* insert in middle of queue */
WSF_QUEUE_NEXT(pElem) = WSF_QUEUE_NEXT(pPrev);
WSF_QUEUE_NEXT(pPrev) = pElem;
}
/* exit critical section */
WSF_CS_EXIT(cs);
}
/*************************************************************************************************/
/*!
* \fn WsfQueueRemove 删除pPrev节点后的pElem节点
*
* \brief Remove an element from a queue. This function is typically used when iterating
* over a queue.
*
* \param pQueue Pointer to queue.
* \param pElem Pointer to element to be removed.
* \param pPrev Pointer to previous element in the queue before element to be removed.
* Note: set pPrev to NULL if pElem is first element in queue.
* \return None.
*/
/*************************************************************************************************/
void WsfQueueRemove(wsfQueue_t *pQueue, void *pElem, void *pPrev)
{
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
WSF_ASSERT(pQueue->pHead != NULL);
WSF_ASSERT(pElem != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
/* if first element */
if (pElem == pQueue->pHead)
{
/* remove from head of queue
如果pElem是头结点则pHead指向pElem的next*/
pQueue->pHead = WSF_QUEUE_NEXT(pElem);
}
else if (pPrev)
{
/* remove from middle of queue, pPrev will never be null
pPrev->next 跳过pElem指向pElem->next*/
WSF_QUEUE_NEXT(pPrev) = WSF_QUEUE_NEXT(pElem);
}
/* if last element */
if (pElem == pQueue->pTail)
{
/* update tail
如果要删除的pElem是尾节点则要更新pTail节点*/
pQueue->pTail = pPrev;
}
/* exit critical section */
WSF_CS_EXIT(cs);
}
/*************************************************************************************************/
/*!
* \fn WsfQueueCount,计算链表有多少个节点
*
* \brief Count the number of elements in a queue.
*
* \param pQueue Pointer to queue.
*
* \return Number of elements in queue.
*/
/*************************************************************************************************/
uint16_t WsfQueueCount(wsfQueue_t *pQueue)
{
wsfQueueElem_t *pElem;
uint16_t count = 0;
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
pElem = pQueue->pHead;
/* iterate over queue */
while (pElem != NULL)
{
count++;
pElem = pElem->pNext;
}
/* exit critical section */
WSF_CS_EXIT(cs);
return count;
}
/*************************************************************************************************/
/*!
* \fn WsfQueueEmpty,判断链表是否有节点
*
* \brief Return TRUE if queue is empty.
*
* \param pQueue Pointer to queue.
*
* \return TRUE if queue is empty, FALSE otherwise.
*/
/*************************************************************************************************/
bool_t WsfQueueEmpty(wsfQueue_t *pQueue)
{
bool_t empty;
WSF_CS_INIT(cs);
WSF_ASSERT(pQueue != NULL);
/* enter critical section */
WSF_CS_ENTER(cs);
empty = (pQueue->pHead == NULL);
/* exit critical section */
WSF_CS_EXIT(cs);
return empty;
}