C语言创建堆栈(顺序栈) 详细分析附源码

举个有意思的例子来解释栈:

 e916a2d337d44cedabbff9ad3420eee6.jpeg

对于这个现象有一种解释是因为printf函数对参数计算的顺序就是从右往左进行的,但是用栈的思想更好理解哈!因为我没有定义任何变量,因此输入的字符会存入键盘的缓冲区,就像栈一样,先输进去的后输出。 

好的,进入正题!!

先把前面引用的头文件和定义的宏拉出来:

#include<stdio.h>
#include<stdlib.h>
const int MAX_SIZE = 5;
#define OVERFLOW 1
#define CAPACITY 1

1、下面定义的栈是用来存储整型的,所以在开辟空间时会强制类型转换成(int*);

2、因为形参是实参的一份临时拷贝,如果传值进入函数,在出去时直接销毁了,因此函数我都用指针去接收,当然,引用也是可以的;

3、

首先先建立大体的框架嘿嘿,也就是我们需要实现的函数:

1、先创建一个结构体Stack

其包含了两个指针:*data 和 *top 

1)、data 指针是为了保存栈,拿来当数组用的,后面的压栈和出栈以及遍历都需要数组实现;

2)、top 指针会一直指向栈顶的元素,以实现栈只能对栈顶元素操作的特性;

3)、至于size是用来表示栈的实际大小,会随着元素的增加而增加。

struct Stack
{
	int* data;//数组首元素地址
	int size;//实际大小
	int* top;//栈顶元素
};

2、初始化函数InitStack

这里很奇怪哈,我原本是想引用的,但是我的VS编译器好像不支持还是咋回事,那所有的函数我就都用指针操作了。

1)、既然要用到数组去存储,那得先给他开个空间,数量是MAX_SIZE,类型是int

2)、因为开辟空间也有可能失败撒,如果失败的话,那我们就直接退出程序了;

         这里的exit函数里的参数有两种情况:0表示正常退出,而非0都是异常退出,我习惯性把OVERFLOW定义为1

3)、当然一般都是开辟成功的哈,那么此时栈里并没有任何元素,因为我把size赋值为0,并且datatop都在一块。

void InitStack(struct Stack* s)
{	
	s->data = (int*)malloc(MAX_SIZE * sizeof(int));
	if (!s->data)//如果分配的内存过大,可能会分配失败
		exit(OVERFLOW);//提前终止程序,后面的语句也就不会执行了
	s->size = 0;
	s->top = s->data;
}

3、销毁栈函数

1)、在main函数里面当然是全部操作完才销毁的,但是因为想让自己养成有开辟必销毁的好习惯,防止内存泄漏,所以写完初始化函数就下意识写销毁函数啦哈哈;

2)、没啥,直接free掉即可。

void DestoryStack(struct Stack* s)//销毁栈函数 
{
	//c不比c++,当存储空间不够用时系统不会直接发生异常并退出程序
	free(s->data);//内存回收
}

4、压栈函数

1)、这里终于要多定义一个变量n来存储压栈的元素啦;

2)、不难想到压栈得要栈有空间才能压啊,因此我们先分析栈没有空间时:

         这里直接让栈顶元素的下标减去栈底元素的下标去和MAX_SIZE比较即可

3)、如果没满直接压栈就行:

         此时的size还是0,刚好就指向第一个元素的下标咯,赋值后,topsize++即可。

void Push(struct Stack* s, int n)
{
	printf("输入压栈的元素:");
	scanf_s("%d", &n);//输入需要入栈的元素
	if (s->top - s->data == MAX_SIZE)//如果栈已经满了
	{
        exit(OVERFLOW);
	}
	else//如果栈没有满,那就压栈
	{
		s->data[s->size] = n;
		s->size++;
		s->top++;
	}
}

5、出栈函数

1)、当然,想要出栈的前提是栈里面得有元素,判断的标志便是data指针指向的位置和top指针指向的位置相同,毕竟每出去一个元素,top指针会往回移一个单位,而实际大小size也会减一;

2)、需要注意的是下标与实际大小的关系是-1哈,但如果是下面这样写就不必了:

  s->top--;
  s->size--; 

  printf("出栈的元素为:%d\n", s->data[s->size]);

3)、因为出栈函数只有出栈成功和失败两种,没有说要扩容之类的,因此还可以定义bool类型,不过需要引用头文件<stdbool.h>

void Pop(struct Stack* s)
{
	if (s->top == s->data)
	{
		printf("栈为空啦!出栈失败捏!\n");
	}
	printf("出栈的元素为:%d\n", s->data[s->size-1]);
	s->top--;//top往后面移一位
	s->size--;//实际大小也应该减一位
}

6、遍历函数

1)、很显然遍历需要用到循环哈,这里就比较浅显啦

void TraveStack(struct Stack* s)
{
	printf("栈里有元素\n");
	for (int i = 0; i < s->size; i++)
	{
		printf("%d ", s->data[i]);
	}
}

其实上面这些函数已经包含了判断是否下溢上溢的情况了,下面给出完整源码

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
const int MAX_SIZE = 5;
#define OVERFLOW 1
struct Stack
{
	int* data;
	int size;
	int* top;
};
void InitStack(struct Stack* s)//初始化函数 
{	
	s->data = (int*)malloc(MAX_SIZE * sizeof(int));
	if (!s->data)
		exit(OVERFLOW);
	s->size = 0;
	s->top = s->data;
}
void DestoryStack(struct Stack* s)//销毁栈函数 
{
	free(s->data);
}
void Push(struct Stack* s, int n)
{
	printf("输入压栈的元素:");
	scanf_s("%d", &n);
	if (s->top - s->data == MAX_SIZE)
	{
		exit(OVERFLOW);
	}
	else
	{
		s->data[s->size] = n;
		s->size++;
		s->top++;
	}
}
bool Pop(struct Stack* s)//出栈函数 
{
	if (s->top == s->data)
	{
		printf("栈为空啦!出栈失败捏!\n");
	}
	printf("出栈的元素为:%d\n", s->data[s->size-1]);
	s->top--;
	s->size--;
}
void TraveStack(struct Stack* s)//遍历栈函数
{
	printf("栈里有元素\n");
	for (int i = 0; i < s->size; i++)
	{
		printf("%d ", s->data[i]);
	}
}
int main()
{
	struct Stack s;
	InitStack(&s);
	Push(&s, 1);
	Push(&s, 1);
	Push(&s, 1);
	Push(&s, 1);
	Push(&s, 1);
	Pop(&s);
	TraveStack(&s);
	DestoryStack(&s);
	return 0;
}

运行结果:

正常情况:

92991482ec0f4180b12158be7c656d93.png

 上溢情况:

cf4ea5d402f54346880c76efd3e4a273.png

 下溢情况:

1caac736bcbc4855a61def9e7289d30d.png

OVER~~~

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值