一、概述
栈在日常开发中有时还是会用到的,比如将递归函数化简为非递归循环、或者解析xml标签。c语言标准库是没有栈这种结构提供的,这里提供一个种栈的实现方法,基于数组实现,支持动态增容,其优势是出入栈速度快、内存连续、和链栈一样可以扩容。
二、原理说明
1、基于数组的栈
栈的最简单的实现是基于数组的,在一个数组中设一个栈顶下标指向栈顶即可,入栈的时候在栈顶的位置写入元素,出栈的时候栈顶下标减小即可。如下图所示:
2、动态增容的栈
在动态增容的数组栈中,出栈入栈的操作和普通的数组栈基本一致,只有当数组中的元素满了的时候,会自动增容,申请一段新的更大的空间,将原有数据拷贝过去。如下图所示:
三、程序设计
1、接口设计
(1)、数据结构
这里直接把结构体定义在头文件,以方便使用者自由选择堆栈内存实例化。需要的字段和普通的栈基本一致。添加了元素大小字段,用以适应不同的数据类型。具体代码如下:
#include<stdint.h>
#include<stddef.h>
/// <summary>
/// 栈
/// </summary>
typedef struct {
/// <summary>
/// 元素大小
/// </summary>
size_t _elementSize;
/// <summary>
/// 元素个数
/// </summary>
size_t _count;
/// <summary>
/// 数据数组
/// </summary>
uint8_t* _data;
/// <summary>
/// 数组容量
/// </summary>
size_t _capacity;
}acf_stack;
(2)、方法
①初始化
栈使用前需要初始化,传入栈对象的指针,以及栈元素的大小即可初始化栈。
/// <summary>
/// 初始化栈
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <param name="_elementSize">元素大小</param>
void acf_stack_init(acf_stack* _this, size_t elementSize);
②反初始化
使用完成后,需要反初始化栈以释放资源。
/// <summary>
/// 反初始化栈
/// </summary>
/// <param name="_this">栈对象的指针</param>
void acf_stack_deinit(acf_stack* _this);
③入栈
将元素压入栈顶,传入的是元素的指针,当元素个数大于栈数组时,会自动增容。
/// <summary>
/// 入栈
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <param name="pElement">元素的指针</param>
void acf_stack_push(acf_stack* _this, void* pElement);
④出栈
栈顶元素出栈,如果栈不为空出栈且返回成功,否则返回失败。
/// <summary>
/// 出栈
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <returns>1出栈成功,0失败</returns>
int acf_stack_pop(acf_stack* _this);
⑤获取栈顶元素
只获取栈顶元素而不出栈。返回栈顶元素的指针。
/// <summary>
/// 获取栈顶元素
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <returns>栈顶元素的指针</returns>
void* acf_stack_peek(acf_stack* _this);
⑥获取元素
根据下标获取元素。返回栈顶元素的指针。
/// <summary>
/// 获取元素
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <param name="index">元素的下标</param>
/// <returns>栈顶元素的指针</returns>
void* acf_stack_get_element(acf_stack* _this, size_t index);
⑦获取栈长度
获取栈中元素的个数。
/// <summary>
/// 获取栈长度
/// </summary>
/// <param name="_this">栈对象的指针</param>
/// <returns>栈长度</returns>
size_t acf_stack_get_count(acf_stack* _this);
⑧获取栈容量
获取当前栈数组长度。
/// <summary>
/// 获取栈容量
/// </summary>
/// <param name="_this"></param>
/// <returns>栈容量</returns>
size_t acf_stack_get_capacity(acf_stack* _this);
⑨清空栈
清除栈中的所有元素。
/// <summary>
/// 清空栈
/// </summary>
/// <param name="_this">栈对象的指针</param>
void acf_stack_clear(acf_stack* _this);
⑩容量收缩
将栈数组长度,收缩至元素个数大小。
/// <summary>
/// 容量收缩
///栈只会根据元素增加增容,不会根据元素减少减容,需要减容则调用此方法。
/// </summary>
/// <param name="_this">栈对象的指针</param>
void acf_stack_trim_excess(acf_stack* _this);
2、关键实现
(1)、动态增容
当栈内元素要大于数组长度时就需要增加数组的长度。
static int acf_stack_set_capacity(acf_stack* _this, size_t newCapacity) {
if (newCapacity < _this->_count)
return 0;
if (newCapacity == _this->_capacity)
return 1;
if (newCapacity)
{
uint8_t* newData = realloc(_this->_data, newCapacity * _this->_elementSize);
if (!newData)
return 0;
_this->_data = newData;
}
else
{
if (_this->_data)
{
free(_this->_data);
_this->_data = NULL;
}
}
_this->_capacity = newCapacity;
return 1;
}
(2)、入栈
入栈前需要判断元素是否已满,已满则需要增容,增容方式是2倍增容。具体实现如下:
void acf_stack_push(acf_stack* _this, void* pData)
{
if (_this->_count == _this->_capacity)
{
if (_this->_capacity < 4)
acf_stack_set_capacity(_this, 4);
else
acf_stack_set_capacity(_this, _this->_capacity *2);
}
memcpy(_this->_data + _this->_count * _this->_elementSize, pData, _this->_elementSize);
_this->_count++;
}
(3)、容量收缩
当栈数组容量过大时,可以手动调用容量收缩方法以减少不必要的空间浪费。参照了c#的stack具体实现如下:
void acf_stack_trim_excess(acf_stack* _this) {
size_t threshold = (size_t)(((double)_this->_capacity) * 0.9);
if (_this->_count < threshold) {
acf_stack_set_capacity(_this, _this->_count);
}
}
3、使用例子
(1)、整型元素
#include "Collection/acf_stack.h"
#include<stdio.h>
int main()
{
acf_stack stack1;
//初始化
acf_stack_init(&stack1, sizeof(int));
//入栈
for (int i = 0; i < 50; i++)
{
acf_stack_push(&stack1, &i);
}
//获取元素个数
printf("count:%d\n", acf_stack_get_count(&stack1));
//获取数组容量
printf("capacity:%d\n", acf_stack_get_capacity(&stack1));
//容量收缩
acf_stack_trim_excess(&stack1);
//获取数组容量
printf("trim capacity:%d\n", acf_stack_get_capacity(&stack1));
printf("pop:");
//出栈
do {
int* pI = acf_stack_peek(&stack1);
if (pI)
printf("%d ", *pI);
} while (acf_stack_pop(&stack1));
printf("\n");
//入栈
for (int i = 0; i < 50; i++)
{
acf_stack_push(&stack1, &i);
}
//遍历栈
printf("traverse:");
for (int i = 0; i < 50; i++)
{
int* pI = acf_stack_get_element(&stack1, i);
if (pI)
printf("%d ", *pI);
}
printf("\n");
//清除元素
acf_stack_clear(&stack1);
printf("clear count:%d\n", acf_stack_get_count(&stack1));
//反初始化
acf_stack_deinit(&stack1);
return 0;
}
(2)、结构体元素
#include "Collection/acf_stack.h"
#include<stdio.h>
typedef struct
{
int i;
int j;
}A;
int main()
{
acf_stack stack2;
//初始化
acf_stack_init(&stack2, sizeof(A));
//入栈
for (int i = 0; i < 50; i++)
{
A a;
a.i = i;
a.j = i;
acf_stack_push(&stack2, &a);
}
//获取元素个数
printf("count:%d\n", acf_stack_get_count(&stack2));
//获取数组容量
printf("capacity:%d\n", acf_stack_get_capacity(&stack2));
//容量收缩
acf_stack_trim_excess(&stack2);
//获取数组容量
printf("trim capacity:%d\n", acf_stack_get_capacity(&stack2));
printf("pop:");
//出栈
do {
A* pA = acf_stack_peek(&stack2);
if (pA)
printf("%d ", pA->i);
} while (acf_stack_pop(&stack2));
printf("\n");
//入栈
for (int i = 0; i < 50; i++)
{
A a;
a.i = i;
a.j = i;
acf_stack_push(&stack2, &a);
}
//遍历栈
printf("traverse:");
for (int i = 0; i < 50; i++)
{
A* pA = acf_stack_get_element(&stack2, i);
if (pA)
printf("%d ", pA->j);
}
printf("\n");
//清除元素
acf_stack_clear(&stack2);
printf("clear count:%d\n", acf_stack_get_count(&stack2));
//反初始化
acf_stack_deinit(&stack2);
return 0;
}
(3)、指针元素
#include "Collection/acf_queue.h"
#include<stdio.h>
#include<stdlib.h>
typedef struct
{
int i;
int j;
}A;
int main()
{
acf_stack stack3;
//初始化
acf_stack_init(&stack3, sizeof(A*));
//入栈
for (int i = 0; i < 50; i++)
{
A* a = malloc(sizeof(A));
a->i = i;
a->j = i;
acf_stack_push(&stack3, &a);
}
//获取元素个数
printf("count:%d\n", acf_stack_get_count(&stack3));
//获取数组容量
printf("capacity:%d\n", acf_stack_get_capacity(&stack3));
//容量收缩
acf_stack_trim_excess(&stack3);
//获取数组容量
printf("trim capacity:%d\n", acf_stack_get_capacity(&stack3));
printf("pop:");
//出栈
do {
A** pA = acf_stack_peek(&stack3);
if (pA)
{
printf("%d ", (*pA)->i);
//释放资源防止内存泄漏
free(*pA);
}
} while (acf_stack_pop(&stack3));
printf("\n");
//入栈
for (int i = 0; i < 50; i++)
{
A* a = malloc(sizeof(A));
a->i = i;
a->j = i;
acf_stack_push(&stack3, &a);
}
//遍历栈
printf("traverse:");
for (int i = 0; i < 50; i++)
{
A** pA = acf_stack_get_element(&stack3, i);
if (pA)
{
printf("%d ", (*pA)->j);
//释放资源防止内存泄漏
free(*pA);
}
}
printf("\n");
//清除元素
acf_stack_clear(&stack3);
printf("clear count:%d\n", acf_stack_get_count(&stack3));
//反初始化
acf_stack_deinit(&stack3);
return 0;
}