各人對(duì)于鏈表的使用并不是很頻繁,偶爾用一下,所以導(dǎo)致每次用都不記得怎么用了,都要重新分析一下邏輯,所以今天在這里記錄一下用法,方便自己也方便用得到的小伙伴查閱。
具體的函數(shù)接口這里就不介紹了,RTThread的API文檔里講的很清楚,按照API接口調(diào)用就可以了,比較容易。
這里主要說(shuō)明rt_list_for_each_entry宏的用法,因?yàn)槊看尉瓦@里不太好理解,這個(gè)宏實(shí)現(xiàn)的功能類似C++中用引用遍歷鏈表,很好用的。而且RTT已經(jīng)給實(shí)現(xiàn)了這種遍歷方法如果不用總感覺(jué)自己虧了點(diǎn)啥
先看一下代碼里此宏的具體定義:
/**
rt_list_for_each_entry - iterate over list of given type @pos: the type * to use as a loop cursor.
@head: the head for your list.
@member: the name of the list_struct within the struct.
*/
#define rt_list_for_each_entry(pos, head, member)
for (pos = rt_list_entry((head)->next, typeof(*pos), member);
&pos->member != (head);
pos = rt_list_entry(pos->member.next, typeof(*pos), member))
乍一看可能有點(diǎn)亂,所以需要稍微梳理一下邏輯,梳理通了也就不復(fù)雜了。此宏具體的實(shí)現(xiàn)功能肯定是要遍歷鏈表內(nèi)的每個(gè)節(jié)點(diǎn)成員,所以核心就是一個(gè)for循環(huán),可每次要用這個(gè)宏的時(shí)候第一反應(yīng)就是這個(gè)宏的三個(gè)參數(shù)是什么意思來(lái)著?要傳入什么?那就要先分析一下for信號(hào)里面的代碼了。
for循環(huán)相信大家都知道了,一般括號(hào)內(nèi)有三條語(yǔ)句(有時(shí)把變化語(yǔ)句放到循環(huán)體內(nèi),這里就只有兩條語(yǔ)句了),第一條語(yǔ)句可以成為初始化語(yǔ)句,初始化循環(huán)變量。第二條語(yǔ)句是循環(huán)條件語(yǔ)句,為真則循環(huán)為假則退出循環(huán)。第三條語(yǔ)句是變量變化語(yǔ)句。
對(duì)應(yīng)到這里的宏,第一條初始化語(yǔ)句為pos = rt_list_entry((head)->next, typeof(*pos), member);,是一個(gè)對(duì)pos的賦值語(yǔ)句,所以這里結(jié)合代碼和上面的注釋就能很容易知道第一個(gè)參數(shù)pos就類似普通for循環(huán)的循環(huán)變量。初始化又用到了另外一個(gè)宏rt_list_entry,此宏相關(guān)的代碼如下:
/**
@brief get the struct for this entry
@param node the entry point
@param type the type of structure
@param member the name of list in structure
/
#define rt_list_entry(node, type, member)
rt_container_of(node, type, member)
/ *
rt_container_of - return the member address of ptr, if the type of ptr is the
struct type.
*/
#define rt_container_of(ptr, type, member)
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
展開后就是一個(gè)減法計(jì)算:(char *)((head)->next) - (unsigned long)(&((typeof(*pos) *)0)->member),其中&((typeof(*pos) *)0)->member的功能是得到pos類型結(jié)構(gòu)體內(nèi)member成員的偏移地址。把0地址看做一個(gè)pos類型的結(jié)構(gòu)體,再取其中任何成員的地址就自然是其偏移地址了。
所以這個(gè)宏的pos參數(shù),傳入的應(yīng)該是你自己定義的鏈表節(jié)點(diǎn)結(jié)構(gòu)體指針,此結(jié)構(gòu)體內(nèi)包含一個(gè)rt_list_t類型的member成員變量。所以第三個(gè)參數(shù)傳入一個(gè)rt_list_t member變量即可。而第二個(gè)參數(shù)head傳入的是鏈表頭節(jié)點(diǎn)內(nèi)的member變量指針。所以這里for循環(huán)內(nèi)的第一條初始化語(yǔ)句就是初始化pos為鏈表內(nèi)第一個(gè)節(jié)點(diǎn)的自定義結(jié)構(gòu)體指針。
理解到這里,第二條語(yǔ)句就很容易理解了,就是判斷遍歷的pos內(nèi)的下一個(gè)節(jié)點(diǎn)指針是不是又回到了head(單向鏈表里是判斷是否為NULL),是則已經(jīng)遍歷一遍,退出循環(huán)。
第三條語(yǔ)句是取pos->member.next也就是下一個(gè)節(jié)點(diǎn)對(duì)于的自己定義結(jié)構(gòu)體指針。
為了方便理解上面的內(nèi)容,下面給出一個(gè)簡(jiǎn)單的例子:
typedef struct __test_node
{
rt_uint16_t a; //可加入任何類型的用戶自定義變量
rt_uint16_t b;
rt_uint16_t c;
rt_list_t member;
}TestNode;
void rt_list_test()
{
TestNode test_head;
rt_list_init(&test_head.member);
for(int i = 0; i < 10; ++i) {
TestNode *new_node = rt_malloc(sizeof(TestNode));
new_node->a = i;
rt_list_insert_before(&test_head.member, &new_node->member);
}
rt_kprintf("list_len:%dn",rt_list_len(&test_head.member));
TestNode *list_pos;
rt_list_t member;
rt_list_for_each_entry(list_pos, &test_head.member, member)
{
rt_kprintf("a:%dn", list_pos->a);
}
}
運(yùn)行結(jié)果:
-
減法器
+關(guān)注
關(guān)注
1文章
26瀏覽量
17296 -
C++語(yǔ)言
+關(guān)注
關(guān)注
0文章
147瀏覽量
7450 -
RTThread
+關(guān)注
關(guān)注
8文章
132瀏覽量
42281 -
for循環(huán)
+關(guān)注
關(guān)注
0文章
61瀏覽量
2827
發(fā)布評(píng)論請(qǐng)先 登錄
rtthread 5.20安裝systemview 2.52a出錯(cuò)的原因?
rtthread 5.20 安裝 systemview 2.52a 出錯(cuò)的原因?
rt_list_for_each_entry編譯失敗怎么解決?
Linux內(nèi)核的鏈表操作
Linux Kernel數(shù)據(jù)結(jié)構(gòu):鏈表
RT-Thread內(nèi)核中單鏈表的使用與實(shí)現(xiàn)
RT-Thread內(nèi)核中雙鏈表的使用與實(shí)現(xiàn)
在RT-Thread中普通鏈表和侵入式鏈表有何區(qū)別
rt_timer_check造成任務(wù)不執(zhí)行問(wèn)題如何處理呢?
淺析RT-Thread中對(duì)象容器與雙鏈表的操作
小編科普一下rtthread鏈表操作的幾個(gè)API與實(shí)用的幾個(gè)宏
請(qǐng)問(wèn)一下鏈表這么使用有問(wèn)題嗎
如何去解決單鏈表rt_slist_for_each宏問(wèn)題呢
RT-Thread中侵入式鏈表的應(yīng)用有哪些呢
FreeRTOS代碼剖析之5:鏈表管理list.c

評(píng)論