对于这个现象有一种解释是因为printf函数对参数计算的顺序就是从右往左进行的,但是用栈的思想更好理解哈!因为我没有定义任何变量,因此输入的字符会存入键盘的缓冲区,就像栈一样,先输进去的后输出。
好的,进入正题!!
先把前面引用的头文件和定义的宏拉出来:
#include
#include
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,并且data和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;
}
3、销毁栈函数
1)、在main函数里面当然是全部操作完才销毁的,但是因为想让自己养成有开辟必销毁的好习惯,防止内存泄漏,所以写完初始化函数就下意识写销毁函数啦哈哈;
2)、没啥,直接free掉即可。
void DestoryStack(struct Stack* s)//销毁栈函数
{
//c不比c++,当存储空间不够用时系统不会直接发生异常并退出程序
free(s->data);//内存回收
}
4、压栈函数
1)、这里终于要多定义一个变量n来存储压栈的元素啦;
2)、不难想到压栈得要栈有空间才能压啊,因此我们先分析栈没有空间时:
这里直接让栈顶元素的下标减去栈底元素的下标去和MAX_SIZE比较即可
3)、如果没满直接压栈就行:
此时的size还是0,刚好就指向第一个元素的下标咯,赋值后,top和size++即可。
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类型,不过需要引用头文件
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
#include
#include
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;
}
运行结果:
正常情况:
上溢情况:
下溢情况:
OVER~~~