我是靠谱客的博主 自由手链,最近开发中收集的这篇文章主要介绍详谈C语言实现堆栈的三种方法前言一、静态数组堆栈二、动态数组堆栈三、链式堆栈四、训练计划总结,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
详谈C语言实现堆栈的三种方法
- 前言
- 一、静态数组堆栈
- 二、动态数组堆栈
- 三、链式堆栈
- 四、训练计划总结
注:本文系岭南师范学院物联网俱乐部原创部分训练计划,转载请保留声明。
前言
本人在此将会带大家去感受一下C语言的数组和指针的魅力,C语言从诞生到至今经久不衰必然有其独特之处,在大学三年虽然学的东西很多,但是从来没有像这几天一样能够集中全力,全心全意地去研究编程语言,原因很简单:不把C语言学得透彻,自己很有可能在这个秋招大潮中失去上岸的机会,为此与小伙伴们通宵达旦也要吃透。以下请仔细看看我的这篇博客,会讲得比以往更透彻一点。
一、静态数组堆栈
/*
引用头文件""和<>的区别
#include" "引用的是你程序目录的相对路径中的头文件。
#include< >引用的是编译器的类库路径里面的头文件。
stdio .h 头文件定义了三个变量类型、一些宏和各种函数来执行输入和输出。
C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息。
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
思路:在静态数组堆栈中,stack_size表示堆栈所能存储的元素的最大值(栈长度),
用top_num作为数组下标来表示堆栈里面的元素,
当top_num == -1的时候表示堆栈为空,
当top_num == STACK_SIZE - 1的时候表示堆栈为满。
push作为入栈函数,入栈的时候,top_num加1,当top_num == 0时表示第一个堆栈元素,
pop作为出栈函数,出栈的时候,top_num减1,这个时候stack_print(打印函数)在打印的时候,将不会再打印这个tom_num已经减一的下标数组,
但是从本质上来讲值还是存在的,只是不打印出来而已
*/
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
/******定义一个栈的长度,其就是堆栈最大容纳元素数量*********/
#define stack_size 50
/******定义一个stack的数组,未初始化,在.bss段********/
static int stack[stack_size];
/******定义一个top_num来作为数组下标,在.data段********/
static int top_num = -1;
/******定义一个判断栈为空的函数,在文本段********/
int empty()
{
return top_num == -1;//如果top_num为-1,其就是(数组/栈)里面没有任何元素
}
/*******定义一个判断栈为满的函数,在文本段********/
int full()
{
return top_num == stack_size-1;//如果top_num为stack_size-1,其就是(数组/栈)里面已经装满元素
}
/********定义一个入栈函数,在文本段******************/
void push(int value)
{
assert(!full());
//先判断栈是否为满,如果未满就允许程序继续执行,如果已满,则停止执行下一步程序
top_num+=1;
//top_num+1,将入栈一个元素
stack[top_num] = value;
//stack[top_num]数组会将value(用户所定的值)一个一个存入数组里面,即入栈。
}
/********定义一个出栈函数,在文本段*******************/
void pop()
{
assert(!empty());
//先判断栈是否为满,如果不为空就允许程序继续执行,如果未空,则停止执行下一步程序
top_num-=1;
//top_num减1,这个时候stack_print(打印函数)在打印的时候,将不会再打印这个tom_num已经减一的下标数组
}
/********定义一个打印函数,在文本段*******************/
void stack_print()
{
int num;
//定义一个num,局部变量,在栈区
num = top_num;
//top_num是一个全局变量,每入栈一个元素或者出栈一个元素,top_num都会发生变化
printf("打印静态数组里面的值:");
if(num == -1)
//num为-1,即top_num为-1,这个时候栈为空
{
printf("栈为空");
}
while(num!=-1)
//num不等于-1,说明有元素入栈,push入栈,等全部入栈后,再通过num--,从数组的下标大到小进行打印
{
printf("%dn",stack[num--]);
}
printf("n");
}
/********定义一个main函数,作为其他函数的一个入口,其位置在文本段*******************/
int main()
{
stack_print();
push(5);
push(4);
push(3);
push(2);
push(1);
printf("入栈后的数据为:");
stack_print();
printf("n");
pop();
pop();
printf("出栈后的数据为:");
stack_print();
printf("n");
return 1;
}
成功截图
二、动态数组堆栈
/*
引用头文件""和<>的区别
#include" "引用的是你程序目录的相对路径中的头文件。
#include< >引用的是编译器的类库路径里面的头文件。
stdio .h 头文件定义了三个变量类型、一些宏和各种函数来执行输入和输出。
C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息。
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
思路:使用stack_size变量取代STACK_SIZE来保存堆栈的长度,
stack数组改成由指针来代替,
创建create_stack函数,先检查堆栈是否已经创建,
然后才根据所需分配内存并检查分配是否成功。
创建destroy_stack函数,先检查堆栈是否存在,
已经释放内存之后把长度和指针变量重新设置为零。
empty 和 full 两个函数中添加了assert()函数,方便用户调试,同时
防止任何堆栈函数在堆栈被创建之前就被调用。
提示:指针与数组是可以互换的
如:char *str 与 char str[]
*/
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
/******定义一个strack指针,在.bss段*********/
static int *stack;
/******定义栈的栈的长度,在.bss段*********/
static int stack_size;
/******定义top_,在.data段*********/
static int top_num = -1;
/******定义一个创建栈的函数,在文本段********/
void create_stack(size_t size)
{
assert(stack_size == 0);
//如果栈长度为0,则运行下一步程序
stack_size = size;
//将自定义的size长度作为栈的总长度
/*
指针变量stack为int *,所以malloc需要加上(int *)进行强制转换,(stack_size * sizeof(int))意思是
栈长度(元素个数,类型为int)与 int的长度进行相乘得到总长度,并且用malloc进行内存分配,最后将stack
将指向其首地址
*/
stack = (int *)malloc(stack_size * sizeof(int));
if (stack == NULL)
{
printf("malloc分配失败");
}
}
/******定义一个销毁栈的函数,在文本段********/
void destroy_stack(void)
{
assert(stack_size
> 0);//如果栈长度不为0,则运行下一步程序
free(stack);
//释放stack指针变量里面的值(malloc已经分配好给stack_size的首地址),但其实这个时候stack还是指向stack_size的首地址
stack_size = 0;
//释放后,人工操作将栈长度赋值为0
stack = NULL;
//指针变量stack变为空指针
}
/*******定义一个判断栈为满的函数,在文本段********/
int full()
{
assert(stack_size > 0);//如果栈长度不为0,则运行下一步程序,这步其实有没有对程度运行没太大影响,只是为了方便用户调试
return top_num == stack_size - 1;//如果top_num为stack_size-1,其是指针指向的stack_size这段存储空间装满了元素,也可以理解为(数组/栈)里面已经装满元素
}
/******定义一个判断栈为空的函数,在文本段********/
int empty()
{
assert(stack_size > 0);//如果栈长度不为0,则运行下一步程序,这步其实有没有对程度运行没太大影响,只是为了方便用户调试
return top_num == -1;
//如果top_num为-1,其是指针指向的stack_size这段存储空间,没有元素,也可以理解为(数组/栈)里面没有元素
}
/********定义一个入栈函数,在文本段******************/
void push(int num)
{
assert(!full());
//先判断栈是否为满,如果未满就允许程序继续执行,如果已满,则停止执行下一步程序
top_num += 1;
//top_num+1,将入栈一个元素
stack[top_num] = num;//stack[top_num]数组会将num(用户所定的值)一个一个存入指针所指向的内存空间(数组)里面,即入栈
}
/********定义一个出栈函数,在文本段*******************/
void pop()
{
assert(!empty());
//先判断栈是否为满,如果不为空就允许程序继续执行,如果未空,则停止执行下一步程序
top_num -= 1;
//top_num减1,这个时候stack_print(打印函数)在打印的时候,将不会再打印这个tom_num已经减一的(指针所指向的内存空间)或者理解为下标数组
}
/********定义一个打印函数,在文本段*******************/
void stack_print()
{
int num;
//定义一个num,局部变量,在栈区
num = top_num;
//top_num是一个全局变量,每入栈一个元素或者出栈一个元素,top_num都会发生变化
printf("打印动态数组里面的值");
if (num == -1)
//num为-1,即top_num为-1,这个时候栈为空
{
printf("这是个空堆栈");
}
while (num != -1)
//num不等于-1,说明有元素入栈,push入栈,等全部入栈后,再通过num--,从指针指向的内存空间(数组的下标)大到小进行打印
{
printf("%dn", stack[num--]);
}
printf("n");
}
/********定义一个main函数,作为其他函数的一个入口,其位置在文本段*******************/
int main()
{
create_stack(50);
stack_print();
push(7);
push(6);
push(5);
push(4);
push(3);
push(2);
push(1);
printf("入栈后的数值:");
stack_print();
printf("n");
pop();
pop();
pop();
printf("出栈后的数值:");
stack_print();
printf("n");
destroy_stack();
return 1;
}
成功截图:
三、链式堆栈
/*
引用头文件""和<>的区别
#include" "引用的是你程序目录的相对路径中的头文件。
#include< >引用的是编译器的类库路径里面的头文件。
stdio .h 头文件定义了三个变量类型、一些宏和各种函数来执行输入和输出。
C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息。
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
思路:
由于只有堆栈顶部元素才可以被访问,因此适用单链表可以很好实现链式堆栈,而且无长度限制。
把一个元素压入堆栈是通过在链表头部添加一个元素实现。
弹出一个元素是通过删除链表头部第一个元素实现。由于没有长度限制,
需要destroy_stack进行释放内存以避免内存泄漏。
提示:指针与数组是可以互换的
如:char *str 与 char str[]
*/
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
/***********定义一个结构体类型,在文本段***************/
typedef struct STACK_VAR
{
int var;
//定义一个变量var,在栈区
struct STACK_VAR *next;
//定义一个结构体指针next(即尾端),在栈区
}stackvar;
static stackvar *stack;
//定义一个结构体指针stack,用来充当桥梁,在栈区
/******定义一个判断栈为空的函数,在文本段********/
int empty()
{
return stack == NULL;
//如果stack为空指针,其就是栈里面没有任何元素
}
/********定义一个入栈函数,在文本段******************/
void push(int var)
{
stackvar *new_var;
//创建一个新的节点new_var(结构体指针)
new_var = (stackvar*)malloc(sizeof(stackvar));
//new_var获取结构体stackvar的首地址,并且new_var指向stackvar结构体
if(new_var == NULL)
//判断new_var是否为空,从而来判断malloc分配的成功与失败
{
printf("malloc分配失败!");
}
new_var->var=var;
//将用户所设定的var,通过形参的方式来到push函数,然后赋值给结构体指针newvar所指向的stackvar结构体里面的var
/*
①本质上将stack里面值(存放着上一个节点首地址),赋值给新建的new_var节点的尾部的next区域(即结构体的里面的next),
从而next_var的next(即结构体)指向上一个节点。
②next_var本来是指向stackvar这个结构体,所以存放值是stack的首地址,赋值给指针变量stack,最后stack指向结构体
提示:每运行一次入栈的代码,都会新建一个节点(结构体指针)和新的结构体地址,前者会指向后者。
*/
new_var->next=stack;
stack=new_var;
}
/********定义一个出栈函数,在文本段*******************/
void pop()
{
stackvar *first_var;
//创建一个新的节点first_var(结构体指针)
assert(!empty());
//先判断栈是否为空,如果不为空就允许程序继续执行,如果为空,则停止执行下一步程序
/*
①本质上将stack里面值(存放着上一个节点首地址),赋值给新建的first_var节点,first_var将会指向上一个节点的首地址,
并获取上一个节点的next存放地址,到自己的next区域
②first_var将自己next区域内容(上一个节点的首地址)的赋值给stack,
stack将会指向上一个节点,最后first_var与其所指向的节点被系统清掉
*/
first_var=stack;
stack=first_var->next;
}
/******定义一个销毁栈的函数,在文本段********/
void stack_all_clear()
{
while(!empty()) //先判断栈是否为空,如果不为空就允许程序继续执行,如果为空,则停止执行下一步程序
pop();
//逐个弹出元素,逐个释放节点内存
{
}
}
/********定义一个打印函数,在文本段*******************/
void stack_print()
{
stackvar *p_var;
//定义一个结构体指针变量,局部变量,在栈区
p_var = stack;
//指针变量stack是一个全局变量,每入栈一个元素或者出栈一个元素,stack都会发生变化
printf("打印链表里面的值");
if(p_var == NULL)
{
printf("栈为空");
}
while(p_var != NULL )
{
printf("%dn",p_var->var);//p_var指向var(用户设定的变量存进了结构体,所以打印的还是用户设定的变量)
p_var = p_var->next;
//next存放的是上一个节点的地址,将这个地址给p_var,让p_var去找上一个地址结构体里面对应的var值,依次从后向前打印
}
printf("n");
}
/********定义一个main函数,作为其他函数的一个入口,其位置在文本段*******************/
int main()
{
stack_print();
push(10);
push(9);
push(8);
push(7);
push(6);
push(5);
push(4);
push(3);
push(2);
push(1);
stack_print();
printf("n");
pop();
pop();
printf("出栈以后的值");
stack_print();
printf("n");
stack_all_clear();
printf("全部清除后");
stack_print();
printf("n");
}
成功截图:
分析图
四、训练计划总结
堆栈(stack)的显著特点是后进先出(Last-In First-Out, LIFO),其实现的方法有三种可选方案:静态数组、动态分配的数组、动态分配的链式结构。
静态数组:特点是要求结构的长度固定,而且长度在编译时候就得确定。其优点是结构简单,实现起来方便而不容易出错。而缺点就是不够灵活以及固定长度不容易控制,适用于知道明确长度的场合。
动态数组:特点是长度可以在运行时候才确定以及可以更改原来数组的长度。优点是灵活,缺点是由此会增加程序的复杂性。
链式结构:特点是无长度上线,需要的时候再申请分配内存空间,可最大程度上实现灵活性。缺点是链式结构的链接字段需要消耗一定的内存,在链式结构中访问一个特定元素的效率不如数组。
目前自己也还不能算是完全掌握吧,继续努力!
最后
以上就是自由手链为你收集整理的详谈C语言实现堆栈的三种方法前言一、静态数组堆栈二、动态数组堆栈三、链式堆栈四、训练计划总结的全部内容,希望文章能够帮你解决详谈C语言实现堆栈的三种方法前言一、静态数组堆栈二、动态数组堆栈三、链式堆栈四、训练计划总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复