queue.h中TAILQ_QUEUE的解析

??queue.h最早來源于FreeBSD逢享,出自某遠(yuǎn)古大神之手,里面定義了5種隊(duì)列,singly-linked lists, lists, simple queues, tail queues, and circular queues。今天我們重點(diǎn)看下里面的tail queues闸衫。

/*  $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $  */
/*  $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $   */

?? 我們來看下代碼結(jié)構(gòu):

/*
 * Tail queue definitions.
 */
#define TAILQ_HEAD(name, type)                      \
struct name {                               \
    struct type *tqh_first; /* first element */         \
    struct type **tqh_last; /* addr of last next element */     \
}

#define TAILQ_HEAD_INITIALIZER(head)                    \
    { NULL, &(head).tqh_first }

#define TAILQ_ENTRY(type)                       \
struct {                                \
    struct type *tqe_next;  /* next element */          \
    struct type **tqe_prev; /* address of previous next element */  \
}

/*
 * tail queue access methods
 */
#define TAILQ_FIRST(head)       ((head)->tqh_first)
#define TAILQ_END(head)         NULL
#define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname)                  \
    (*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field)                \
    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head)                       \
    (TAILQ_FIRST(head) == TAILQ_END(head))

#define TAILQ_FOREACH(var, head, field)                 \
    for((var) = TAILQ_FIRST(head);                  \
        (var) != TAILQ_END(head);                   \
        (var) = TAILQ_NEXT(var, field))

#define TAILQ_FOREACH_REVERSE(var, head, headname, field)       \
    for((var) = TAILQ_LAST(head, headname);             \
        (var) != TAILQ_END(head);                   \
        (var) = TAILQ_PREV(var, headname, field))

/*
 * Tail queue functions.
 */
#define TAILQ_INIT(head) do {                       \
    (head)->tqh_first = NULL;                   \
    (head)->tqh_last = &(head)->tqh_first;              \
} while (0)

#define TAILQ_INSERT_HEAD(head, elm, field) do {            \
    if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)    \
        (head)->tqh_first->field.tqe_prev =         \
            &(elm)->field.tqe_next;             \
    else                                \
        (head)->tqh_last = &(elm)->field.tqe_next;      \
    (head)->tqh_first = (elm);                  \
    (elm)->field.tqe_prev = &(head)->tqh_first;         \
} while (0)

#define TAILQ_INSERT_TAIL(head, elm, field) do {            \
    (elm)->field.tqe_next = NULL;                   \
    (elm)->field.tqe_prev = (head)->tqh_last;           \
    *(head)->tqh_last = (elm);                  \
    (head)->tqh_last = &(elm)->field.tqe_next;          \
} while (0)

#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {      \
    if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
        (elm)->field.tqe_next->field.tqe_prev =         \
            &(elm)->field.tqe_next;             \
    else                                \
        (head)->tqh_last = &(elm)->field.tqe_next;      \
    (listelm)->field.tqe_next = (elm);              \
    (elm)->field.tqe_prev = &(listelm)->field.tqe_next;     \
} while (0)

#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {           \
    (elm)->field.tqe_prev = (listelm)->field.tqe_prev;      \
    (elm)->field.tqe_next = (listelm);              \
    *(listelm)->field.tqe_prev = (elm);             \
    (listelm)->field.tqe_prev = &(elm)->field.tqe_next;     \
} while (0)

#define TAILQ_REMOVE(head, elm, field) do {             \
    if (((elm)->field.tqe_next) != NULL)                \
        (elm)->field.tqe_next->field.tqe_prev =         \
            (elm)->field.tqe_prev;              \
    else                                \
        (head)->tqh_last = (elm)->field.tqe_prev;       \
    *(elm)->field.tqe_prev = (elm)->field.tqe_next;         \
} while (0)

#define TAILQ_REPLACE(head, elm, elm2, field) do {          \
    if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
        (elm2)->field.tqe_next->field.tqe_prev =        \
            &(elm2)->field.tqe_next;                \
    else                                \
        (head)->tqh_last = &(elm2)->field.tqe_next;     \
    (elm2)->field.tqe_prev = (elm)->field.tqe_prev;         \
    *(elm2)->field.tqe_prev = (elm2);               \
} while (0)

??我們?cè)倏磦€(gè)例子:

 #include <stdio.h>
 #include <stdlib.h>
 struct QUEUE_ITEM {
    int value;
    TAILQ_ENTRY(QUEUE_ITEM) entries;
};
TAILQ_HEAD(TAIL_QUEUE, QUEUE_ITEM) queue_head;
int main(int argc, char **argv) {
    struct QUEUE_ITEM *item;
    struct QUEUE_ITEM *tmp_item;

    TAILQ_INIT(&queue_head);
    int i = 0;
    for (i = 5; i < 10; i += 2) {
        item = (struct QUEUE_ITEM*)malloc(sizeof(QUEUE_ITEM));
        item->value = i;
        TAILQ_INSERT_TAIL(&queue_head, item, entries);
    }

    struct QUEUE_ITEM *ins_item;
    ins_item = (struct QUEUE_ITEM*)malloc(sizeof(QUEUE_ITEM));

    ins_item->value = 100;
    TAILQ_INSERT_BEFORE(item, ins_item, entries);


    tmp_item = TAILQ_FIRST(&queue_head);
    printf("first element is %d\n", tmp_item->value);

    tmp_item = TAILQ_NEXT(tmp_item, entries);
    printf("next element is %d\n", tmp_item->value);

    tmp_item = TAILQ_NEXT(tmp_item, entries);
    printf("next element is %d\n", tmp_item->value);

    tmp_item = TAILQ_NEXT(tmp_item, entries);
    printf("next element is %d\n", tmp_item->value);

    getchar();
}

??內(nèi)存分布如圖:


無(wú)標(biāo)題.png

??我們先看TAILQ_HEAD:
tqh_first為隊(duì)列第一個(gè)元素的地址献丑;
tqh_last為最后一個(gè)元素tqe_next的地址末捣;
tqh_last指向的指針為0;
??再看TAILQ_ENTRY:
tqe_next為隊(duì)列下一個(gè)元素的地址创橄;
tqe_prev為隊(duì)列上一個(gè)元素tqe_next的地址塔粒;
tqe_prev指向的指針為當(dāng)前元素的地址;

??于是筐摘,我們?cè)谀X子里形成了對(duì)這個(gè)隊(duì)列的大致結(jié)構(gòu)卒茬。

??下面我們逐一深度分析隊(duì)列的操作。

#define TAILQ_HEAD_INITIALIZER(head) { NULL, &(head).tqh_first }
#define TAILQ_INIT(head) do {                       \
    (head)->tqh_first = NULL;                   \
    (head)->tqh_last = &(head)->tqh_first;              \
} while (0)

??這兩個(gè)宏代表了隊(duì)列的初始化咖熟,將tqh_first賦值為NULL圃酵,再將tqh_last 賦值為tqh_first的地址,如此做馍管,當(dāng)?shù)谝淮翁砑釉貢r(shí)郭赐,對(duì)tqh_last 指向的元素操作既是對(duì)tqh_first操作。

#define TAILQ_FIRST(head)       ((head)->tqh_first)
#define TAILQ_END(head)         NULL
#define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname)                  \
    (*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field)                \
    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

??TAILQ_LAST确沸,TAILQ_PREV這兩個(gè)宏可能比較難理解捌锭。
??先看TAILQ_LAST,我們思考下罗捎,如何獲取最后一個(gè)元素的地址观谦,從圖上看,最后一個(gè)元素的tqe_prev指向的值即是最后一個(gè)元素的地址桨菜,由文章前面可知豁状,最后一個(gè)元素的tqe_next值為(head)->tqh_last,如何從tqe_next獲取到tqe_prev指向的值倒得,因?yàn)門AILQ_ENTRY與TAILQ_HEAD的內(nèi)存布局是一樣的泻红,稍加思考,于是就有了這個(gè)宏表達(dá)式霞掺。TAILQ_PREV類似谊路。
??這里要注意,TAILQ_ENTRY與TAILQ_HEAD的內(nèi)存布局是一樣的菩彬,為什么TAILQ_LAST和TAILQ_PREV中不將(head)->tqh_last)轉(zhuǎn)換為TAILQ_HEAD而不是轉(zhuǎn)換為TAILQ_ENTRY是因?yàn)門AILQ_ENTRY這個(gè)結(jié)構(gòu)體是直接定義在你用戶定義的結(jié)構(gòu)體中缠劝,無(wú)法獲取他的類型來進(jìn)行轉(zhuǎn)換。
??其他的宏都比較簡(jiǎn)單挤巡,直來直去剩彬,比較好理解酷麦,這里就不詳述了矿卑。以后有問題再作記錄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沃饶,一起剝皮案震驚了整個(gè)濱河市母廷,隨后出現(xiàn)的幾起案子轻黑,更是在濱河造成了極大的恐慌,老刑警劉巖琴昆,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氓鄙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡业舍,警方通過查閱死者的電腦和手機(jī)抖拦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舷暮,“玉大人态罪,你說我怎么就攤上這事∠旅妫” “怎么了复颈?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)沥割。 經(jīng)常有香客問我耗啦,道長(zhǎng),這世上最難降的妖魔是什么机杜? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任帜讲,我火速辦了婚禮,結(jié)果婚禮上椒拗,老公的妹妹穿的比我還像新娘舒帮。我一直安慰自己,他們只是感情好陡叠,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布玩郊。 她就那樣靜靜地躺著,像睡著了一般枉阵。 火紅的嫁衣襯著肌膚如雪译红。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天兴溜,我揣著相機(jī)與錄音侦厚,去河邊找鬼。 笑死拙徽,一個(gè)胖子當(dāng)著我的面吹牛刨沦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膘怕,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼想诅,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起来破,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤篮灼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后徘禁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诅诱,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年送朱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娘荡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驶沼,死狀恐怖它改,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情商乎,我是刑警寧澤央拖,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站鹉戚,受9級(jí)特大地震影響鲜戒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抹凳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一遏餐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赢底,春花似錦失都、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至洽损,卻和暖如春庞溜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碑定。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工流码, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人延刘。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓漫试,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親碘赖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驾荣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 已經(jīng)踐行過一半了外构,回顧45天,還是有許多遺憾秘车,安排許多目標(biāo)也只完成一部分典勇。辛苦了45天劫哼,我還是要感謝高能組...
    杏好有你_360d閱讀 173評(píng)論 0 0
  • 學(xué)習(xí)筆記: 西方大師 1. 彼得·德魯克《卓有成效的管理者》等相關(guān)著作 德魯克被稱為大師中的大師叮趴,他不僅是現(xiàn)代管理...
    JYangel閱讀 310評(píng)論 0 0
  • 一、倆权烧、二眯亦、?三般码、四 奧妻率,錯(cuò)了 一、二板祝、三宫静、四、五券时、六孤里、七……呃 八! GOOD 橘洞,關(guān)燈捌袜,碎覺
    彥澀閱讀 128評(píng)論 2 1
  • 《寫給2019》 在哈爾濱的楊,近來可好炸枣?朋友中最會(huì)玩的就屬你了虏等,我想說,注意安全适肠,注意保暖霍衫,哈爾濱很冷。2018...
    周瑀閱讀 298評(píng)論 1 0
  • 很喜歡也很害怕~~
    愛上你的風(fēng)姑娘閱讀 166評(píng)論 0 0