栈
栈是个好东西,为什么呢?因为,站着说话不腰疼!!!
栈(stack)是限定仅在表尾进行插入或者删除操作的线性表。因此,对栈来说,表尾端有其特殊的含义,称为栈顶(top),相应的,表头端称为栈底(bottom)。不含元素的空包称为空栈。栈的特点为尾端数据后进先出(Last In First Out,简称LIFO结构)。
栈的存储类型有两种,一种是线性存储,即开辟一段连续的存储空间,与数组类似,利用top,bottom指针进行操作,一般情况下这种方式使用的是最多的。另外一种是栈的链式存储,这种存储方式用的不是很多。本篇文章采用的是第一种存储方式。下面用代码具体实现栈的操作。
1.结构
线性存储的栈的结构和以前的链式表的结构不怎么一样,因为他开辟了一段连续的空间,在整个程序中设定另一个结构仅仅记录栈顶、栈底以及栈的容量。
typedef int SElemType;
typedef int Status;
typedef struct
{
SElemType* top; // 栈顶指针
SElemType* base; // 栈底指针
int statacsize; // 栈的容量
} SqStack;
#define STACK_INIT_SIZE 5 //初始化栈的容量
#define STACK_INCE_SIZE 2 //每次栈增量的大小
#define OK 1
#define ERROR 0
2.重点
- 一定要理解线性存储栈在内存中的形态,这样有助于理解栈操作的机制,最好画图理解,下面上图。
栈初始化后,开辟了5个int类型的空间,每个int类型占4个字节,共计开辟了20个字节的空间,起始地址为0x13b81e0,结束地址为0x13b81f3,s->top、s->base都指向栈底。中间压入了一个数据后,栈顶指针对应的单元赋值1,然后栈顶指针加1。最右边的栈满的状态,大家一定要理解,当栈压入了5个元素后,栈顶指针指向0x13b81f4,这个时候,栈顶指针已经超出了所分配的单元,这种情况下会出问题吗?这个问题我也想了很久,我个人认为不会出现问题,在后边的代码中可以看到,栈顶指针指向0x13b81f4仅仅用到了探测边界,并没有取值,而且这段地址是程序在堆区分配的地址,不是代码区,所以不会出现问题。 - 栈满的情况下要扩容,就要用到realloc函数,这个函数会出现两种情况,一种是在原有的内存分配空间后边刚好有大于增量大小的内存,就直接在原先的基础上向后扩,简单的说是栈底指针不变。第二种情况是原有的内存分配空间后边没有适合增量大小的内存,系统会在堆区重新开辟一段空间,空间长度为原先已经分配过的内存长度 加上 增量部分,分配好后这段内存的首地址已经发生变化了,必须重新指定s->base的地址,然后在根据扩容前的容量修改s->top指针,然后再修改扩容后的容量。
通过上图可以看到,小容量的栈在扩容的情况下,s->base的地址基本不会改变,通过对压栈、出栈的地址变化可以更加清楚看清内存的变化。
3.代码
sqstack.h
#pragma once
typedef int SElemType;
typedef int Status;
typedef struct
{
SElemType* top; // 栈顶指针
SElemType* base; // 栈底指针
int statacsize;
} SqStack;
#define STACK_INIT_SIZE 5
#define STACK_INCE_SIZE 2
#define OK 1
#define ERROR 0
//初始化栈
Status Init_Stack(SqStack* s);
//压栈、入栈
Status Push(SqStack* s, SElemType e);
//弹栈、出栈
Status Pop(SqStack* s, SElemType* e);
//销毁栈
Status DestoryStack(SqStack* s);
//清空栈
Status ClearStack(SqStack* s);
//栈为空返回1,否则返回0
Status IsStackEmpty(SqStack* s);
//栈的长度
int StackLength(SqStack* s);
//获得最顶端元素
Status GetTopData(SqStack* s, SElemType* e);
sqstack.c
#pragma once
#include"sqstack.h"
#include<stdio.h>
#include<stdlib.h>
extern SqStack SStack;
//初始化栈
Status Init_Stack(SqStack* s)
{
s->base = (SElemType*)malloc(sizeof(SElemType) * STACK_INIT_SIZE);
printf("分配空间大小:%d\n", sizeof(SElemType) * STACK_INIT_SIZE);
if (!s->base)
{
printf("内存分配失败。。\n");
return ERROR;
}
s->top = s->base;
s->statacsize = STACK_INIT_SIZE;
printf("s.base=0x%x,size = %d\n",s->base,sizeof(s->base));
return OK;
}
//压栈、入栈
Status Push(SqStack* s, SElemType e)
{
int n = 0;
n = s->top - s->base;
if (n >= s->statacsize)
{
//重新分配连续内存
s->base = (SElemType*)realloc(s->base, (s->statacsize + STACK_INCE_SIZE)*sizeof(SElemType));
if (!s->base)
{
printf("内存分配失败。。\n");
return ERROR;
}
s->top = s->base + s->statacsize;
s->statacsize = s->statacsize + STACK_INCE_SIZE;
}
*s->top++ = e;
return OK;
}
//弹栈、出栈
Status Pop(SqStack* s, SElemType *e)
{
if (s->top == s->base)
{
printf("栈空了,没东西了,弹。。弹个毛线。\n");
return ERROR;
}
*e = *(--s->top);
return OK;
}
//销毁栈
Status DestoryStack(SqStack* s)
{
if (!s->base) return ERROR;
free(s->base);
s->base = NULL;
s->top = s->base;
s->statacsize = 0;
}
//清空栈
Status ClearStack(SqStack* s)
{
if (!s->base) return ERROR;
s->top = s->base;
return OK;
}
//栈为空返回1,否则返回0
Status IsStackEmpty(SqStack* s)
{
if (s->base == s->top)
{
return OK;
}
else
{
return ERROR;
}
}
//栈的长度
int StackLength(SqStack* s)
{
if (!s->base) return -1;
return s->top - s->base;
}
//获得最顶端元素
Status GetTopData(SqStack* s, SElemType* e)
{
if (!s->base) return ERROR;
*e = *(s->top - 1);
return OK;
}
测试代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"sqstack.h"
SqStack SStack ;
int main()
{
SElemType n;
SStack.base = NULL;
SStack.top = SStack.base;
SStack.statacsize = 0;
Init_Stack(&SStack);
printf("初始化栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n",SStack.base,SStack.top,SStack.statacsize);
Push(&SStack, 1);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
Push(&SStack, 2);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
Push(&SStack, 3);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
Push(&SStack, 4);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
Push(&SStack, 5);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
Push(&SStack, 6);
printf(" 压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
printf(" ---------------------------------------------------------------------\n");
printf(" 出栈:\n");
while (SStack.base != SStack.top)
{
Pop(&SStack, &n);
printf(" 出栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d, \t%d\n", SStack.base, SStack.top, SStack.statacsize,n);
}
printf("\n");
Pop(&SStack, &n);
DestoryStack(&SStack);
system("pause");
}