咱們固然能夠根據棧的特性,向實現鏈表同樣實現棧。可是,若是可以複用已經通過實踐證實的可靠數據結構來實現棧,不是能夠更加高效嗎?數據結構
so,今天咱們就複用Linux內核鏈表,實現棧這樣的數據結構。函數
要實現的功能很簡單,以下(項目中如需更多功能,可自行添加):測試
/* stack.h */ #ifndef _STACK_H_ #define _STACK_H_ #include "list.h" #define get_stack_top(pos, head, member) \ list_entry(pos->member.prev, typeof(*pos), member) void stack_creat(struct list_head *list); void stack_push(struct list_head *new, struct list_head *head); void stack_pop(struct list_head *entry); int get_satck_size(struct list_head *head); #endif /* _STACK_H_ */
/* stack.c */ #include "stack.h" void list_del_tail(struct list_head *head) { __list_del(head->prev->prev,head); } void stack_creat(struct list_head *list) { INIT_LIST_HEAD(list); } void stack_push(struct list_head *new, struct list_head *head) { list_add_tail(new,head); } void stack_pop(struct list_head *head) { struct list_head *list = head->prev; /* 保存鏈表的最後節點 */ list_del_tail(head); /* 尾刪法 */ INIT_LIST_HEAD(list); /* 從新初始化刪除的最後節點,使其指向自身 */ } int get_satck_size(struct list_head *head) { struct list_head *pos; int size = 0; if (head == NULL) { return -1; } list_for_each(pos,head) { size++; } return size; }
咱們先來講,棧的建立:spa
很是簡單,和內核鏈表同樣,僅僅是將鏈表指針指向自身。設計
棧的push(入棧)操做:指針
也很是得簡單,根據棧的先進後出特性,咱們採用尾插法,這樣最後插入的節點也就位於最後,也就是棧頂。code
棧的pop(出棧)操做:blog
出棧只能操做棧頂元素,因此咱們使用尾刪法,將內核鏈表的尾部節點刪除,就實現了出棧操做,可是內核鏈表沒有直接實現尾刪法,不過,咱們已經在前面的隨筆中對內核鏈表進行了分析,顯然能夠利用內核已經實現了的__list_del函數,稍微改變一下參數,就能夠實現尾刪法了。get
獲取棧的大小:io
原理也很是簡單,循環遍歷鏈表,計數增長便可。
獲得棧頂元素:
爲何這裏我沒有使用函數,而是使用宏呢?這和內核鏈表的邏輯是一致的。由於若是要寫成函數,我必須知道使用棧的人定義的數據類型,若是我定義成void *,又不能使用內核鏈表的list_entry獲取容器結構地址的宏了,因此,我將獲取棧頂元素設計爲宏,這樣我能夠不定義數據類型,靠用戶輸入。
如今,咱們經過很是簡單的一點代碼複用內核鏈表實現了棧,下面看看測試用例:
#include <stdio.h> #include <stdlib.h> #include "stack.h" #include "list.h" struct person { int age; struct list_head list; }; int main(int argc,char **argv) { int i; int num =5; struct person *p; struct person head; struct person *pos,*n; stack_creat(&head.list); p = (struct person *)malloc(sizeof(struct person )*num); for (i = 0;i < num;i++) { p->age = i*10; stack_push(&p->list,&head.list); p++; } struct person test; test.age = 100; stack_pop(&head.list); stack_pop(&head.list); stack_push(&test.list,&head.list); printf("==========>\n"); list_for_each_entry_safe_reverse(pos,n,&head.list,list) { printf("%p age = %d\n",pos,pos->age); } printf("%p age = %d\n",get_stack_top(pos,&head.list,list), get_stack_top(pos,&head.list,list)->age); printf("size = %d\n",get_satck_size(&head.list)); return 0; }
運行結果:
經過複用內核鏈表,能夠很是快速高效地實現不少其餘數據結構,因此內核鏈表必定要充分掌握。
增長判斷棧是否爲空的函數:
bool is_empt_stack(struct list_head *head) { return list_empty(head); }
c語言中bool能夠包含#include <stdbool.h>,c99能夠直接使用_Bool。