一、概述

栈在日常开发中有时还是会用到的,比如将递归函数化简为非递归循环、或者解析xml标签。c语言标准库是没有栈这种结构提供的,这里提供一个种栈的实现方法,基于数组实现,支持动态增容,其优势是出入栈速度快、内存连续、和链栈一样可以扩容。

二、原理说明

1、基于数组的栈

栈的最简单的实现是基于数组的,在一个数组中设一个栈顶下标指向栈顶即可,入栈的时候在栈顶的位置写入元素,出栈的时候栈顶下标减小即可。如下图所示:

动态 增加容器 端口映射命令 什么叫动态增容_栈

2、动态增容的栈

在动态增容的数组栈中,出栈入栈的操作和普通的数组栈基本一致,只有当数组中的元素满了的时候,会自动增容,申请一段新的更大的空间,将原有数据拷贝过去。如下图所示:

动态 增加容器 端口映射命令 什么叫动态增容_动态 增加容器 端口映射命令_02

三、程序设计

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;
}