Linux内核链表复用实现栈

 

 咱们固然能够根据栈的特性,向实现链表同样实现栈。可是,若是可以复用已经通过实践证实的可靠数据结构来实现栈,不是能够更加高效吗?数据结构

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。

相关文章
相关标签/搜索