堆栈工具
一、堆栈是什么?
1.定义:堆栈是一种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
2.特征
1)先进后出,后进先出。
2)单入口、单出口、出入同口。
像一个子弹匣,第一颗子弹放进去,一定是锃亮锃亮的。因为最后一发射出的子弹可能是留给自己的。
3.堆栈其他的基本属性
堆栈的控制头:
1)数据空间(真正的堆栈空间)
2)容量 (capacity)
3)栈底指针(bottom)(可以不要)
4)栈顶指针(top)
堆栈的基本操作:
1)入栈
2)出栈
堆栈的表示:
1)线性存储结构----数组
2)非线性存储结构----链表
二、堆栈工具
既然堆栈是·一种数据存储结构,那么,要有工具性,起码这个存储的数据类型可以自定义的、多类型的。
在上一篇线性表工具里,采用了用户自定义USER_TYPE这种方式。还有其他方法吗?
1.堆栈数据存储空间实现
思考: 由于数据类型是用户自定义的,所以直接申请这类数据空间是不行的。那么如果将“数据的存储空间的首地址”存储起来可不可以?
这种想法有这些优点:
1)不论数据类型多复杂,多长,仅保存其首地址,使得数组中每个元素长度固定为4b;
2)不论用户数据类型是什么,其首地址的都可以看成void* !
3)在允许void * 的值(数组元素的值, 即数据首地址的值)相同的情况下,可以不用再申请void*元素,节省一定内存空间。
所以:使用的数据类型是 void **data;
//解释
typedef struct STU {
int score;
...
}STU;
void **data = NULL;
data = (void **)calloc(sizeof(void *), n);
int num = 30;
char str[8] = "12345678";
STU stu1 = {...};
data[0] = #
data[1] = str; (&str[0] <=> &*(str + 0) <=> str)
data[2] = stu1;
//下面是图片
所以,控制头就产生了:
typedef struct STACK {
void **stackData;
int capacity;
int top;
}STACK;
编程好习惯:boolean值。
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
初始化堆栈空间如下:
boolean initStack(STACK **stack, int capacity) {
if (NULL == stack || NULL != *stack
|| capacity <= 0 || capacity > MAX_STACK_CAPACITY) {
//分析: STACK stack 是一个结构体,也可以看成第一个空链
//STACK *stack 是结构体指针,4b空间。即结构体首地址值。
//STACK **stack 是指向结构体指针的指针,4b空间,即结构体指针的地址值
//NULL == stack, 结构体指针不存在,
//NULL != *stack 结构体指针指向的空间存在,说明已经申请了结构体,无需再初始化
return FALSE; //初始化失败
}
(*stack) = (STACK *)calloc(sizeof(STACK), 1);//申请一个堆栈控制空间
if (NULL == *stack) {
return FALSE; //防止申请失败
}
(*stack)->stackData = (void **)calloc(sizeof(void *), capacity);//申请堆栈数据存储空间
if (NULL == (*stack)->stackData) { //申请失败
free(*stack); //销毁堆栈控制空间
*stack = NULL;// 堆栈控制空间指针变为0,销毁
return FALSE;
}
(*stack)->capacity = capacity;
(*stack)->top = 0;
return TRUE; //申请成功
}
有初始化,就必须有销毁:注意:顺序
//销毁堆栈空间
void destoryStack(STACK **stack) {
if (NULL == stack || NULL == *stack) {
return; //销毁过了
}
free((*stack)->stackData);//必须先销毁堆栈数据存储空间
free(*stack);//销毁堆栈控制空间
*stack = NULL;// 堆栈控制空间指针变为0,销毁
}
如果先销毁堆栈控制空间,那么数据存储空间的首地址就不存在了,无法销毁了。
2.堆栈工具基本功能实现
1)栈的使用情况
//栈空?
boolean isStackEmpty(const STACK *stack) {
return NULL == stack || stack->top <= 0;//栈顶指针只在最底下。栈空
}
//栈满?
boolean isStackFull(const STACK *stack) {
return NULL == stack || stack->top >= stack->capacity;//超出容量
}
2)栈的基本操作
**这里需要注意:**你的栈顶指针只在哪个位置
这是我的栈顶指针位置:
//入栈
boolean push(STACK *stack, void *data) {
if (NULL == stack || isStackFull(stack)) {
return FALSE;
}
stack->stackData[stack->top++] = data;
//滞后自增,将数据存储在栈顶指针所致空间,然后栈顶指针向上冒一个
return TRUE;
}
//出栈
void *pop(STACK * stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stackData[--stack->top];
//滞前自增,先栈顶指针向下走一个,再将栈顶元素弹出来
}
//读取栈顶元素
void *readTop(const STACK *stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stackData[stack->top - 1];
}
这样一来,一个堆栈工具已经基本实现。
堆栈工具可以使用的地方,比如说:表达式的运用。
编译通过,最后一个错误的意思是缺少主函数。毕竟这是一个工具,使用者这需要obj文件就行了。
三、总结
1.这里最大的收获就是那个void **数据类型。我只要你数据的首地址,不管你的数据类型。很好很强大的解决了用户自定义数据类型。
2.结构决定其功能。
感谢指导老师:铁血教主
笔者水平有限,目前只能描述以上问题,如果有其他情况,可以留言,有错误,请指教,有继续优化的,请分享,谢谢!
完整代码如下:
stack.h代码:
#ifndef _STICK_STACK_H
#define _STICK_STACK_H
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define MAX_STACK_CAPACITY 1000000 //堆栈空间最大容量
typedef struct STACK {
void **stackData;
int capacity;
int top;
}STACK;
boolean initStack(STACK **stack, int capacity);
void destoryStack(STACK **stack);
boolean isStackEmpty(const STACK *stack);
boolean isStackFull(const STACK *stack);
boolean push(STACK *stack, void *data);
void *pop(STACK *stack);
void *readTop(const STACK *stack);
#endif
stack.c代码:
#include <stdio.h>
#include <malloc.h>
#include "stack.h"
boolean initStack(STACK **stack, int capacity) {
if (NULL == stack || NULL != *stack
|| capacity <= 0 || capacity > MAX_STACK_CAPACITY) {
//分析: STACK stack 是一个结构体,也可以看成第一个空链
//STACK *stack 是结构体指针,4b空间。即结构体首地址值。
//STACK **stack 是指向结构体指针的指针,4b空间,即结构体指针的地址值
//NULL == stack, 结构体指针不存在,
//NULL != *stack 结构体指针指向的空间存在,说明已经申请了结构体,无需再初始化
return FALSE; //初始化失败
}
(*stack) = (STACK *)calloc(sizeof(STACK), 1);//申请一个堆栈控制空间
if (NULL == *stack) {
return FALSE; //防止申请失败
}
(*stack)->stackData = (void **)calloc(sizeof(void *), capacity);//申请堆栈数据存储空间
if (NULL == (*stack)->stackData) { //申请失败
free(*stack); //销毁堆栈控制空间
*stack = NULL;// 堆栈控制空间指针变为0,销毁
return FALSE;
}
(*stack)->capacity = capacity;
(*stack)->top = 0;
return TRUE; //申请成功
}
//销毁堆栈空间
void destoryStack(STACK **stack) {
if (NULL == stack || NULL == *stack) {
return; //销毁过了
}
free((*stack)->stackData);//必须先销毁堆栈数据存储空间
free(*stack);//销毁堆栈控制空间
*stack = NULL;// 堆栈控制空间指针变为0,销毁
}
//栈空?
boolean isStackEmpty(const STACK *stack) {
return NULL == stack || stack->top <= 0;//栈顶指针只在最底下。栈空
}
//栈满?
boolean isStackFull(const STACK *stack) {
return NULL == stack || stack->top >= stack->capacity;//超出容量
}
//入栈
boolean push(STACK *stack, void *data) {
if (NULL == stack || isStackFull(stack)) {
return FALSE;
}
stack->stackData[stack->top++] = data;
//滞后自增,将数据存储在栈顶指针所致空间,然后栈顶指针向上冒一个
return TRUE;
}
//出栈
void *pop(STACK *stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stackData[--stack->top];
//滞前自增,先栈顶指针向下走一个,再将栈顶元素弹出来
}
//读取栈顶元素
void *readTop(const STACK *stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stackData[stack->top - 1];
}
2020年02.19 家