• 正文
    • sys/queue.h的使用
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

嵌入式代碼江湖里,queue.h 竟是隱藏的神兵!

3小時前
35
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是雜燴君。

queue.h是Linux、FreeBSD中的一個頭文件。

FreeBSD:FreeBSD 是一種類 UNIX操作系統(tǒng)。

這是一個很實用的頭文件,因為這個頭文件里全是宏定義操作,所以其不僅可以使用在Linux/嵌入式Linux項目中,也可以很方便地使用在單片機項目中。

它使用宏實現(xiàn)了如下數(shù)據(jù)結(jié)構(gòu):

    SLIST:單向無尾鏈表LIST:雙向無尾鏈表STAILQ:單向有尾鏈表(可作隊列使用)TAILQ:雙向有尾鏈表(可作隊列使用)

所有的數(shù)據(jù)結(jié)構(gòu)都支持如下功能:

    在鏈表頭插入節(jié)點在任意節(jié)點后插入節(jié)點刪除節(jié)點遍歷節(jié)點

我們可以在Linux系統(tǒng)的如下路徑中找到這個頭文件:

/usr/include/sys/queue.h

也可以通過如下網(wǎng)址查看:https://code.woboq.org/userspace/glibc/misc/sys/queue.h.html

sys/queue.h的使用

下面我們基于SLIST來演示其使用。

SLIST相關(guān)宏定義:

/*
?* Singly-linked List definitions.
?*/
#define?SLIST_HEAD(name, type) ? ? ? ? 
struct name { ? ? ? ? ? ? ? ? ? 
?struct type *slh_first;?/* first element */? ? ? ? 
}

#define?SLIST_HEAD_INITIALIZER(head) ? ? ? 
?{ NULL }

#define?SLIST_ENTRY(type) ? ? ? ? ?
struct { ? ? ? ? ? ? ?
?struct type *sle_next;?/* next element */? ? ?
}

/*
?* Singly-linked List functions.
?*/
#define?SLIST_INIT(head) do { ? ? ? ? 
?(head)->slh_first = NULL; ? ? ? ? 
} while (/*CONSTCOND*/0)

#define?SLIST_INSERT_AFTER(slistelm, elm, field) do { ? 
?(elm)->field.sle_next = (slistelm)->field.sle_next; ? 
?(slistelm)->field.sle_next = (elm); ? ? ? 
} while (/*CONSTCOND*/0)

#define?SLIST_INSERT_HEAD(head, elm, field) do { ? ?
?(elm)->field.sle_next = (head)->slh_first; ? ? 
?(head)->slh_first = (elm); ? ? ? ? 
} while (/*CONSTCOND*/0)

#define?SLIST_REMOVE_HEAD(head, field) do { ? ? ?
?(head)->slh_first = (head)->slh_first->field.sle_next; ?
} while (/*CONSTCOND*/0)

#define?SLIST_REMOVE(head, elm, type, field) do { ? ?
?if?((head)->slh_first == (elm)) { ? ? ? 
? SLIST_REMOVE_HEAD((head), field); ? ? ?
?} ? ? ? ? ? ? ? 
?else?{ ? ? ? ? ? ? ?
? struct type *curelm = (head)->slh_first; ? ?
? while(curelm->field.sle_next != (elm)) ? ? 
? ?curelm = curelm->field.sle_next; ? ? 
? curelm->field.sle_next = ? ? ? ?
? ? ? curelm->field.sle_next->field.sle_next; ? ?
?} ? ? ? ? ? ? ? 
} while (/*CONSTCOND*/0)

#define?SLIST_FOREACH(var, head, field) ? ? ? 
?for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)

/*
?* Singly-linked List access methods.
?*/
#define?SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define?SLIST_FIRST(head) ((head)->slh_first)
#define?SLIST_NEXT(elm, field) ((elm)->field.sle_next)

下面我們通過實例來操作。

首先,創(chuàng)建鏈表頭節(jié)點、其它節(jié)點結(jié)構(gòu)體,用到SLIST_HEAD與SLIST_ENTRY這兩個宏定義:

#define?ELEM_TYPE int

/* 鏈表節(jié)點 */
typedef?struct?node?
{
? ? ELEM_TYPE data;
? ? SLIST_ENTRY(node) field;?
}node_st;

/* 鏈表頭 */
typedef?SLIST_HEAD(head, node)?head_st;

鏈表數(shù)據(jù)域類型我們定義為int,field表示的是指針域。

① 創(chuàng)建一個頭結(jié)點:

/* 創(chuàng)建鏈表頭節(jié)點并初始化 */
head_st *head = (head_st *)malloc(sizeof(head_st));
SLIST_INIT(head);

 

頭節(jié)點:不存任何數(shù)據(jù)的空節(jié)點,通常作為鏈表的第一個節(jié)點。

② 在鏈表頭部分別插入節(jié)點node1、node2:

/* 頭插法插入一個節(jié)點node1 */
node_st *node1 = (node_st *)malloc(sizeof(node_st));
node1->data =?1;
SLIST_INSERT_HEAD(head, node1, field);

/* 頭插法插入一個節(jié)點node2 */
node_st *node2 = (node_st *)malloc(sizeof(node_st));
node2->data =?2;
SLIST_INSERT_HEAD(head, node2, field);

頭指針:永遠(yuǎn)指向鏈表第一個節(jié)點的位置。

SLIST_INSERT_HEAD是從鏈表頭部插入節(jié)點,新節(jié)點總是從頭結(jié)點之后插入。

③ 在鏈表節(jié)點node2之后插入節(jié)點node3:

node_st *node3 = (node_st *)malloc(sizeof(node_st));
node3->data =?3;
SLIST_INSERT_AFTER(node2, node3, field);

SLIST_INSERT_AFTER是從指定節(jié)點slistelm之后插入新節(jié)點elm。

④ 遍歷鏈表:

node_st *tmp_elm;
SLIST_FOREACH(tmp_elm, head, field)
{
?printf("%d ", tmp_elm->data);
}

輸出為tmp_elm,訪問tmp_elm即可。

⑤ 刪除某個節(jié)點node2

SLIST_REMOVE(head, node2, node, field);
free(node2);
node2 =?NULL;

⑥ 銷毀整個鏈表

while?(!SLIST_EMPTY(head))?
{ ? ?
? ? node_st *p = SLIST_FIRST(head);
? ? SLIST_REMOVE_HEAD(head, field);
? ??free(p);
? ? p =?NULL;
}
free(head);
head =?NULL;

完整測試代碼:

#include?<stdio.h>
#include?<stdlib.h>
#include?<sys/queue.h>

#define?ELEM_TYPE int

/* 鏈表節(jié)點 */
typedefstruct?node?
{
? ? ELEM_TYPE data;
? ? SLIST_ENTRY(node) field;?
}node_st;

/* 鏈表頭 */
typedef?SLIST_HEAD(head, node)?head_st;

int?main(void)
{
? ??/* 創(chuàng)建鏈表頭節(jié)點并初始化 */
? ? head_st *head = (head_st *)malloc(sizeof(head_st));
? ? SLIST_INIT(head);

? ??/* 頭插法插入一個節(jié)點node1 */
? ? node_st *node1 = (node_st *)malloc(sizeof(node_st));
? ? node1->data =?1;
? ? SLIST_INSERT_HEAD(head, node1, field);

? ??/* 頭插法插入一個節(jié)點node2 */
? ? node_st *node2 = (node_st *)malloc(sizeof(node_st));
? ? node2->data =?2;
? ? SLIST_INSERT_HEAD(head, node2, field);

? ??/* 遍歷打印當(dāng)前鏈表節(jié)點 */
? ??printf("list:n");
? ? node_st *tmp_elm;
? ? SLIST_FOREACH(tmp_elm, head, field)
? ? {
? ? ? ??printf("%d ", tmp_elm->data);
? ? }
? ??printf("n");

? ??/* 尾插法插入一個節(jié)點node3 */
? ??printf("insert node3:n");
? ? node_st *node3 = (node_st *)malloc(sizeof(node_st));
? ? node3->data =?3;
? ? SLIST_INSERT_AFTER(node2, node3, field);
? ? SLIST_FOREACH(tmp_elm, head, field)
? ? {
? ? ? ??printf("%d ", tmp_elm->data);
? ? }
? ??printf("n");

? ??/* 刪除node2 */
? ??printf("delete node2:n");
? ? SLIST_REMOVE(head, node2, node, field);
? ??free(node2);
? ? node2 =?NULL;
? ? SLIST_FOREACH(tmp_elm, head, field)
? ? {
? ? ? ??printf("%d ", tmp_elm->data);
? ? }
? ??printf("n");

? ??/* 銷毀鏈表 */
? ??while?(!SLIST_EMPTY(head))?
? ? { ? ?
? ? ? ? node_st *p = SLIST_FIRST(head);
? ? ? ? SLIST_REMOVE_HEAD(head, field);
? ? ? ??free(p);
? ? ? ? p =?NULL;
? ? }
? ??free(head);
? ? head =?NULL;

? ??return0;
}

編譯、運行:

運行結(jié)果與我們上面分析的一致。

本次我們只分享queue.h里最簡單的數(shù)據(jù)結(jié)構(gòu)。其它幾種數(shù)據(jù)結(jié)構(gòu)的使用例子及相關(guān)宏說明可以通過man命令查看。

man是Linux下的幫助命令。

我們輸入?man queue?即可查到queue.h的相關(guān)說明:

可以看到,man命令很強大,可查到queue的幫助說明很詳細(xì),有宏的說明及使用示例等。

以上就是本次的分享,我們下期見~

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

本公眾號專注于嵌入式技術(shù),包括但不限于C/C++、嵌入式、物聯(lián)網(wǎng)、Linux等編程學(xué)習(xí)筆記,同時,公眾號內(nèi)包含大量的學(xué)習(xí)資源。歡迎關(guān)注,一同交流學(xué)習(xí),共同進步!