rt-thread中的鍊錶是帶表頭節點的雙向迴圈鍊錶結構,它的表頭節點與之前的部落格《雙向迴圈鍊錶》中介紹的表頭節點不同,之前部落格介紹的表頭節點與後繼節點結構是一致的,這是因為指標型別問題,前面介紹過的鍊錶都是前驅節點指向後繼節點的首位址,即指向節點結構體的指標。rt-thread鍊錶節點中的指標並不是指向節點首位址(這種說法並不嚴謹,儘管實際上它確實不是指向節點首位址),而是指向節點中的list結構體元素,這種鍊錶結構讓鍊錶更加靈活。
rt-thread中的鍊錶指標定義為rt_list_t
,而不是節點型別,這就可以使鍊錶的操作(例如:插入、刪除)不用依賴整個節點,不用管節點結構體中成員的具體情況,甚至可以將不同型別的節點插入鍊錶。這就是為什麼表頭節點與其他節點不同,在操作鍊錶時卻沒有帶來額外的麻煩。
節點與節點之間可以不一致,那麼不管鍊錶中節點用於存放何種資料,它的size
有多大,表頭節點都可以只存放用於實現演算法所需的輔助資料,在節點比較大時可以節省記憶體空間,還可以一致實現不同鍊錶的表頭節點。
rt-thread中不同object鍊錶的表頭節點都存放於rt_object_container
陣列當中,各個鍊錶在初始化時都初始化為帶表頭節點的空表,不同object
的節點不同,同一object
的節點也不盡相同。但表頭都採用了統一結構:
/**
* the information of the kernel object
*/struct rt_object_information
;
之前介紹的雙向迴圈鍊錶的插入、刪除等操作都依賴於節點的結構體型別,要準確找到next
、prev
等指標在節點中的位置,需要將指向節點的指標定義為該節點型別指標。在諸多不同鍊錶,甚至同一鍊錶中節點結構不盡相同的情況下,這種方式變得不可行。在節點結構體成員的順序性不做要求的情況下,可以將prev
、next
兩個指標放在節點起始位置,在遍歷整個鍊錶時,進行強轉,從而實現不同鍊錶、不同節點之間的遍歷。但是rt-thread中,每個節點的起始位址都用於存放"parent"
以實現類似物件導向語言中的一些特性。且每個"class"
的"parent"
長度不一,導致之後存放指標的位置不定,強轉這真是個餿主意?
鍊錶節點的插入
在前文中提到,rt-thread鍊錶節點中都包含rt_list_t
結構,且prev
、next
指標都指向各自前驅節點和後繼節點中的rt_list_t
成員,那鍊錶插入就不麻煩了,只要將《雙向迴圈鍊錶》的操作方法稍微改動以下,將節點起始位址改為rt_list_t
節點起始位址。
 rt-thread中專門實現了插入相關的內聯函式:
/**
* @brief insert a node after a list
* * @param l list to insert it
* @param n new node to be inserted
*/rt_inline void
rt_list_insert_after
(rt_list_t *l, rt_list_t *n)
/**
* @brief insert a node before a list
* * @param n new node to be inserted
* @param l list to insert it
*/rt_inline void
rt_list_insert_before
(rt_list_t *l, rt_list_t *n)
鍊錶節點的刪除
節點的刪除也是同樣的道理,在刪除相關節點的時候,不再是將後繼節點(前驅節點)的起始位址賦給前驅節點(後繼節點)的指標,而是將後繼節點(前驅節點)中rt_list_t
成員的位址賦給前驅節點(後繼節點)的指標。
/**
* @brief remove node from list.
* @param n the node to remove from the list.
*/rt_inline void
rt_list_remove
(rt_list_t *n)
鍊錶節點元素訪問
既然rt_list_t
成員是存放在節點中部或是尾部,且不同型別的節點rt_list_t
成員位置還不一樣,那在遍歷整個鍊錶時,獲得的是後繼節點(前驅節點)的rt_list_t
成員的位址,那如何根據rt_list_t
成員的位址訪問節點中其他元素。
儘管不同型別節點中rt_list_t
成員位置不定,但是在確定型別節點中,rt_list_t
成員的偏移是固定的,在獲取rt_list_t
成員位址的情況下,計算出rt_list_t
成員在該節點中的偏移,即(rt_list_t成員位址)-(rt_list_t成員偏移)=節點起始位址
。關鍵在於如何計算不同型別節點中rt_list_t成員偏移
。rt-thread中給出的相應演算法如下:
/**
* 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)))
該巨集替換之後,ptr
是該節點中rt_list_t
成員首位址,member
是rt_list_t
結構體成員,type
則是該節點的結構體型別。
在0
位址處強制轉化為type
結構體型別,取得的相關member
成員位址即為member
成員在type
型別結構體中的偏移。
type型別結構體
成員位址
int element1
0x00000000
int element2
0x00000004
int element3
0x00000008
int element4
040000000c
int element5
0x00000010……
member
member成員位址……
將計算獲得的節點首位址強制轉化為相應的結構體型別便可訪問相應的資料。
RT Thread 雙向鍊錶分析
從鍊錶刪除節點函式 rt list remove 鍊錶節點元素訪問 雙向鍊錶也叫雙鏈表,是鍊錶的一種,是在作業系統中常用的資料結構,它的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅,其頭指標 head 是唯一確定的。所以,從雙向鍊錶中的任意乙個結點開始,都可以很方便地訪問它的前驅結點和後繼...
Rt Thread之雙向鍊錶學習
struct rt list node typedef struct rt list node rt list t 第一步 看的時候主要是看原來的圖 l next 就是node2,因為要新插入乙個,node2往後退 所以l next prev 就指向新插入的節點。node2的前乙個是n 第二步 原來...
rt thread隨筆4補充 雙向鍊錶
typedef struct dnode dnode,doublelist 1 struct rt list node 2 6typedef struct rt list node rt list t rt list t 型別的節點裡面有兩個 rt list t 型別的節點指標 next 和 pre...