18.實現雙向鏈表

本文爲實現雙向鏈表,爲以後的 就緒隊列全部任務隊列準備數據結構。

一共包含兩個文件,放在 /lib/kernel 目錄下。

list.h

#ifndef __LIB_KERNEL_LIST_H
#define __LIB_KERNEL_LIST_H

#define NULL ((void*)0)
#define bool int
#define true 1
#define false 0

#define offset(struct_type,member) (int)(&((struct_type*)0)->member)
#define elem2entry(struct_type, struct_member_name, elem_ptr) \
	 (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name))

/**********   定義鏈表結點成員結構   ***********
*結點中不需要數據成元,只要求前驅和後繼結點指針*/
struct list_elem {
   struct list_elem* prev; // 前軀結點
   struct list_elem* next; // 後繼結點
};

/* 鏈表結構,用來實現隊列 */
struct list {
/* head是隊首,是固定不變的,不是第1個元素,第1個元素爲head.next */
   struct list_elem head;
/* tail是隊尾,同樣是固定不變的 */
   struct list_elem tail;
};

/* 自定義函數類型function,用於在list_traversal中做回調函數 */
typedef bool (function)(struct list_elem*, int arg);

void list_init (struct list*);
void list_insert_before(struct list_elem* before, struct list_elem* elem);
void list_push(struct list* plist, struct list_elem* elem);
void list_iterate(struct list* plist);
void list_append(struct list* plist, struct list_elem* elem);  
void list_remove(struct list_elem* pelem);
struct list_elem* list_pop(struct list* plist);
bool list_empty(struct list* plist);
uint32_t list_len(struct list* plist);
struct list_elem* list_traversal(struct list* plist, function func, int arg);
bool elem_find(struct list* plist, struct list_elem* obj_elem);
#endif

(1)先是一些宏定義。

(2)list_elem 是鏈表的結點。看着是不是有點怪異,這個結點沒有數據項,只有兩個指針,指向 前驅 和 後繼。

(3)list 是鏈表結構,裏面有固定的隊首和隊尾。隊首的後繼纔是隊列的第一個元素,隊尾的前驅纔是隊列的最後一個元素。

(4)其次就是函數的聲明。

接下來是 list.c

list.c

#include "../std_int.h"
#include "list.h"
#include "../../kernel/my_interrupt.h"

/* 初始化雙向鏈表list */
void list_init (struct list* list) {
   list->head.prev = NULL;
   list->head.next = &list->tail;
   list->tail.prev = &list->head;
   list->tail.next = NULL;
}

/* 把鏈表元素elem插入在元素before之前 */
void list_insert_before(struct list_elem* before, struct list_elem* elem) { 
   enum intr_status old_status = intr_disable();

/* 將before前驅元素的後繼元素更新爲elem, 暫時使before脫離鏈表*/ 
   before->prev->next = elem; 

/* 更新elem自己的前驅結點爲before的前驅,
 * 更新elem自己的後繼結點爲before, 於是before又回到鏈表 */
   elem->prev = before->prev;
   elem->next = before;

/* 更新before的前驅結點爲elem */
   before->prev = elem;

   intr_set_status(old_status);
}

/* 添加元素到列表隊首,類似棧push操作 */
void list_push(struct list* plist, struct list_elem* elem) {
   list_insert_before(plist->head.next, elem); // 在隊頭插入elem
}

/* 追加元素到鏈表隊尾,類似隊列的先進先出操作 */
void list_append(struct list* plist, struct list_elem* elem) {
   list_insert_before(&plist->tail, elem);     // 在隊尾的前面插入
}

/* 使元素pelem脫離鏈表 */
void list_remove(struct list_elem* pelem) {
   enum intr_status old_status = intr_disable();
   
   pelem->prev->next = pelem->next;
   pelem->next->prev = pelem->prev;

   intr_set_status(old_status);
}

/* 將鏈表第一個元素彈出並返回,類似棧的pop操作 */
struct list_elem* list_pop(struct list* plist) {
   struct list_elem* elem = plist->head.next;
   list_remove(elem);
   return elem;
} 

/* 從鏈表中查找元素obj_elem,成功時返回true,失敗時返回false */
bool elem_find(struct list* plist, struct list_elem* obj_elem) {
   struct list_elem* elem = plist->head.next;
   while (elem != &plist->tail) {
      if (elem == obj_elem) {
	 return true;
      }
      elem = elem->next;
   }
   return false;
}

/* 把列表plist中的每個元素elem和arg傳給回調函數func,
 * arg給func用來判斷elem是否符合條件.
 * 本函數的功能是遍歷列表內所有元素,逐個判斷是否有符合條件的元素。
 * 找到符合條件的元素返回元素指針,否則返回NULL. */
struct list_elem* list_traversal(struct list* plist, function func, int arg) {
   struct list_elem* elem = plist->head.next;
/* 如果隊列爲空,就必然沒有符合條件的結點,故直接返回NULL */
   if (list_empty(plist)) { 
      return NULL;
   }

   while (elem != &plist->tail) {
      if (func(elem, arg)) {		  // func返回ture則認爲該元素在回調函數中符合條件,命中,故停止繼續遍歷
	 return elem;
      }					  // 若回調函數func返回true,則繼續遍歷
      elem = elem->next;	       
   }
   return NULL;
}

/* 返回鏈表長度 */
uint32_t list_len(struct list* plist) {
   struct list_elem* elem = plist->head.next;
   uint32_t length = 0;
   while (elem != &plist->tail) {
      length++; 
      elem = elem->next;
   }
   return length;
}

/* 判斷鏈表是否爲空,空時返回true,否則返回false */
bool list_empty(struct list* plist) {		// 判斷隊列是否爲空
   return (plist->head.next == &plist->tail ? true : false);
}

都是數據結構中鏈表的基本操作。不再贅述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章