我们当然可以根据栈的特性,向实现链表一样实现栈。但是,如果能够复用已经经过实践证明的可靠数据结构来实现栈,不是可以更加高效吗?

so,今天我们就复用Linux内核链表,实现栈这样的数据结构。

要实现的功能很简单,如下(项目中如需更多功能,可自行添加):

/* stack.h */

#ifndef _STACK_H_
#define _STACK_H_ #include "list.h" #define get_stack_top(pos, head, member) \
list_entry((head)->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 = ; if (head == NULL) {
return -;
} list_for_each(pos,head) {
size++;
} return size;
}

我们先来说,栈的创建:

非常简单,和内核链表一样,仅仅是将链表指针指向自身。

栈的push(入栈)操作:

也非常得简单,根据栈的先进后出特性,我们采用尾插法,这样最后插入的节点也就位于最后,也就是栈顶。

栈的pop(出栈)操作:

出栈只能操作栈顶元素,所以我们使用尾删法,将内核链表的尾部节点删除,就实现了出栈操作,但是内核链表没有直接实现尾删法,不过,我们已经在前面的随笔中对内核链表进行了分析,显然可以利用内核已经实现了的__list_del函数,稍微改变一下参数,就可以实现尾删法了。

获取栈的大小:

原理也非常简单,循环遍历链表,计数增加即可。

得到栈顶元素:

为什么这里我没有使用函数,而是使用宏呢?这和内核链表的逻辑是一致的。因为如果要写成函数,我必须知道使用栈的人定义的数据类型,如果我定义成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 =;
struct person *p;
struct person head;
struct person *pos,*n; stack_creat(&head.list); p = (struct person *)malloc(sizeof(struct person )*num); for (i = ;i < num;i++) {
p->age = i*;
stack_push(&p->list,&head.list);
p++;
} struct person test;
test.age = ; 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 ;
}

运行结果:

通过复用内核链表,可以非常快速高效地实现很多其他数据结构,所以内核链表一定要充分掌握。

增加判断栈是否为空的函数:

bool is_empt_stack(struct list_head *head)
{
return list_empty(head);
}

c语言中bool可以包含#include <stdbool.h>,c99可以直接使用_Bool。

Linux内核链表复用实现栈的更多相关文章

  1. Linux内核链表复用实现队列

    有了前面Linux内核复用实现栈的基础,使用相同的思想实现队列,也是非常简单的.普通单链表复用实现队列,总会在出队或入队的时候有一个O(n)复杂度的操作,大多数采用增加两个变量,一个head,一个ta ...

  2. 链表的艺术——Linux内核链表分析

    引言: 链表是数据结构中的重要成员之中的一个.因为其结构简单且动态插入.删除节点用时少的长处,链表在开发中的应用场景许多.仅次于数组(越简单应用越广). 可是.正如其长处一样,链表的缺点也是显而易见的 ...

  3. Linux 内核 链表 的简单模拟(1)

    第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...

  4. C语言 Linux内核链表(企业级链表)

    //Linux内核链表(企业级链表) #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> ...

  5. 深入分析 Linux 内核链表--转

    引用地址:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html 一. 链表数据结构简介 链表是一种常用的组织有序数据 ...

  6. Linux 内核链表

    一 . Linux内核链表 1 . 内核链表函数 1.INIT_LIST_HEAD:创建链表 2.list_add:在链表头插入节点 3.list_add_tail:在链表尾插入节点 4.list_d ...

  7. linux内核链表分析

    一.常用的链表和内核链表的区别 1.1  常规链表结构        通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...

  8. 深入分析 Linux 内核链表

    转载:http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/   一. 链表数据结构简介 链表是一种常用的组织有序数据的数据结构,它通过指 ...

  9. Linux 内核 链表 的简单模拟(2)

    接上一篇Linux 内核 链表 的简单模拟(1) 第五章:Linux内核链表的遍历 /** * list_for_each - iterate over a list * @pos: the & ...

随机推荐

  1. Linux 常用命令(根据自己的理解随时更新)

    1. linux 目录解释系统启动必须: /boot:存放的启动 Linux 时使用的内核文件,包括连接文件以及镜像文件. /etc:存放所有的系统需要的配置文件和子目录列表,更改目录下的文件可能会导 ...

  2. web服务器-apache

    一.apache详解 1. 概述 apache是世界上使用排名第一的web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的web服务器端软件之一.它快 ...

  3. 这是一份非常适合收藏的Android进阶/面试重难点整理

    写在前面 记得我大二时“不务正业”地自学Android并跟了老师做项目,到大三开始在目前的公司实习,至今毕业已有几年多,学习Android已经6.7年多了!但总感觉知识点很零散,并且不够深入,遇到瓶颈 ...

  4. LGOJP3193 [HNOI2008]GT考试

    \(f[i][j]\)表示当前摆放到第\(i\)位,然后当前的匹配长度为\(j\) \(f[i][j]=\sum {f[i][k]*g[k][j]}\) \(g[i][j]\)表示将长度为\(i\)的 ...

  5. 201671010426 孙锦喆 实验十四 团队项目评审&课程学习总结

    徐明锦 徐明锦 2 95 2019-06-30T14:54:00Z 2019-06-30T14:54:00Z 9 608 3472 28 8 4072 14.00 Clean Clean false ...

  6. 2019牛客暑期多校训练营(第一场):XOR(线性基)

    题意:给定数组,求所有异或起来为0的集和的大小之和. 思路:由于是集合大小,我们换成考虑每个元素在多少个集合里有贡献. 先生成线性基. 对于没有插入线性基的元素x,贡献是2^(N-base-1),因为 ...

  7. Exception的异常分类与处理

    一. 异常:  1:错误的分类          1)Error:错误(不可控),一般指的是系统方面的异常,比如 蓝屏,内存溢出,jvm运行环境出现了问题.          2) Exception ...

  8. sqoop2相关实例:hdfs和mysql互相导入(转)

    原文地址:http://blog.csdn.net/dream_an/article/details/74936066 超详细讲解Sqoop2应用与实践 2017年07月10日 20:06:57 阅读 ...

  9. reduce要素与适用总结

    要素: 1.高阶函数:reduce: 2.处理函数:reducer: 3.数据:可以是具体数据.签名相同的普通函数.签名相同的高阶函数: reduce(reducer, datas(data or f ...

  10. k8gege的Ladon使用笔记

    自己今天看到了这个工具,感觉挺实用的,尝试学习用法 资产扫描模块 初级用法: Ladon.exe 192.168.1.8/24 OnlinePC(扫当前机器所处C段的存活主机,其它模块同理) 总结:在 ...