1.函数的声明与自定义
linkstack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define PUSH 1
#define POP 2
#define GETTOP 3
#define PRINT 4
#define EXIT 0
typedef char ElemType;
typedef struct linknode {
ElemType data;
struct linknode* next;
}LinkStNode;
#ifndef __LINKSTACK_H__
#define __LINKSTACK_H__
void menu();
void InitStack(LinkStNode*& s); //初始化
void DestoryStack(LinkStNode*& s); //销毁栈
bool StackEmpty(LinkStNode* s); //判断栈空
bool Push(LinkStNode*& s, ElemType e); //入栈
bool Pop(LinkStNode*& s, ElemType&e); //出栈
bool gettop(LinkStNode* s, ElemType& e);//或取栈顶元素
void Print(LinkStNode* s, ElemType& e);//打印栈中元素
#endif // !__LINKSTACK_H__
2.链式栈的各函数操作
链式栈.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "linkstack.h"
void menu() {
printf("************************************************\n");
printf("******1.push **********\n");
printf("******2.pop 3.gettop********\n");
printf("******4.print 0.exit**********\n");
printf("************************************************\n");
}
//初始化头结点
void InitStack(LinkStNode*& s) {
s = (LinkStNode*)malloc(sizeof(LinkStNode));
s->next = NULL;
}
//销毁栈空间
void DestoryStack(LinkStNode*& s) {
LinkStNode* pre = s; //定义指针pre指向头结点
LinkStNode* p = s->next; //定义指针p指向首节点
while (p != NULL) { //判断是否为空,首节点为空则整个栈为空,直接通过指针pre释放s开辟的空间
free(pre); //先释放头结点
pre = p; //领pre指向p,(也就是指向了头结点指向的下一个节点)
p = pre->next; //指针p也同步向后移
}
free(pre); //释放最后一个节点的空间
pre = NULL; //指针赋空,避免野指针(但感觉这里也没太大必要,因为这只是个局部变量,跳出就没了)
}
//判断栈是否为空
bool StackEmpty(LinkStNode* s) {
return(s->next = NULL);
}
//进栈操作
bool Push(LinkStNode*& s, ElemType e) {
LinkStNode* p; //开辟一个节点空间
p = (LinkStNode*)malloc(sizeof(LinkStNode));
p->data = e; //头插法(头插法就会把最先进的元素排到尾部,可以实现栈的先进后出的特点)
p->next = s->next;
s->next = p;
return true;
}
//因为是链式栈,先进后出的特点,所以是头插法,同时也是首节点先出
bool Pop(LinkStNode*& s, ElemType& e) {
LinkStNode* p=NULL; //创建结构体指针p,这个指针作用是指向出栈元素,避免出栈元素节点没指针指造成内存泄漏
if (s->next == NULL) { //判断是否空栈
return false;
}
p = s->next; //s为头节点,头结点指向的就是首节点,这里新建节点等于首节点
e = p->data; //提取出首节点元素赋值给e,这里的作用是可以设置参数e用于告诉用户出栈了什么元素
s->next = p->next; //头结点此时指向第二个节点,此时就把首节点独立出来了
free(p); //释放掉p节点空间
p = NULL;
return true;
}
//获取栈顶元素
bool gettop(LinkStNode* s, ElemType& e) {
if (s->next == NULL) {
return false;
}
e = s->next->data;
return true;
}
//将栈中元素注意打印出来
void Print (LinkStNode* s, ElemType& e) {
if (s->next == NULL) { //判断栈是否为空
printf("the stack is empty\n");
return;
}
printf("栈中元素有:");
while (s->next != NULL) {
e = s->next->data; //s为头结点,s的next即为首节点,打印首节点
printf("%c ", e);
s = s->next;
}
printf("\n");
}
3.运行
TEST.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "linkstack.h"
//链式栈
void test() {
LinkStNode* s = NULL;
int input = 0;
ElemType e;
InitStack(s);
do {
menu();
printf("please choose mode:");
scanf("%d", &input);
//if (StackEmpty(s)) {
// printf("此栈为空\n");
//}
//else {
// printf("此栈不为空\n");
//}
switch (input) {
case PUSH:
printf("请输入要入栈的值:");
getchar();
scanf("%c", &e); //因为e的类型为char型,只能接受一个字符(不是char数组),只能使用%c,使用%s在销毁空间时会出现堆栈溢出的错误,而使用%c会因为前面的换行导致跳过
if (Push(s, e)) { //所以要使用getchar把换行吞掉。(我理解是%s开辟的是属于字符串空间是大于1字节,而链式栈中设定一个节点为char型,仅为1字节,在销毁空间后,%s多余的空间并没能随着栈节点销毁而销毁就造成栈溢出
printf("入栈成功\n");
}
else {
printf("入栈失败\n");
}
break;
case POP:
if (Pop(s, e)) {
printf("出栈成功\n");
}
else {
printf("出栈失败");
}
printf("出栈元素为:%c\n", e);
break;
case GETTOP:
gettop(s, e);
printf("此时栈顶元素为:%c \n", e);
break;
case PRINT:
printf("栈的内容为:");
Print(s, e);
break;
case EXIT:
DestoryStack(s);
break;
default:
printf("模式选择错误,请重新输入!\n");
break;
}
} while (input);
}
int main() {
test();
return 0;
}
4.结果
入栈与出栈都符合栈的先进后出的特点