mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
178 lines
6.1 KiB
Markdown
178 lines
6.1 KiB
Markdown
## 介绍
|
||
|
||
栈是一种先进后出(First In Last Out,FILO)的数据结构,一般情况下只有一个出入口,从栈顶进从栈顶出。入栈push,出栈pop。
|
||
varch的堆栈和队列实现逻辑很类似,队列有两个端口,循环模式数据在指定空间内循环的存储,而堆栈只有一个端口,一般情况从栈顶出入栈,栈底数据在指定空间的低地址段而栈顶数据在高地址端。
|
||
* 容量
|
||
容量也就是在使用过程中最多能存多少个栈数据,比如,容量为10的栈,最多能存10个栈数据,存满后,再想入栈就入不了。varch的栈存储是连续地址的,是有限容量的栈。
|
||
|
||
|
||
## 接口
|
||
|
||
### 创建和删除stack对象
|
||
```c
|
||
stack_t stack_create(int dsize, int capacity, void *base);
|
||
void stack_delete(stack_t stack);
|
||
#define stack(type, capacity) // 为了更简便的使用,对stack_create套一层宏定义
|
||
#define _stack(stack) // 对stack_delete套一层宏定义,并在stack删除后置为空
|
||
```
|
||
其中**stack_t**为stack的结构体,创建方法则会返回一个stack对象,创建失败则返回NULL,其中`dsize`传入数据的大小,`capacity`传入栈容量,`*base`传入缓冲区地址(可以不传入,不传入的话就会自动分配capacity大小的空间以来存储栈数据)。删除方法则是删除传入的stack对象。创建方法和删除应该成对使用,创建出来在结束使用应该删除掉。
|
||
```c
|
||
void test(void)
|
||
{
|
||
stack_t stack = stack(int, 10); // 定义并创建一个int型容量为10的stack
|
||
_stack(stack); // 成对使用,用完即删除
|
||
}
|
||
```
|
||
|
||
### stack的入栈和出栈
|
||
```c
|
||
int stack_push(stack_t stack, void* data);
|
||
int stack_pop(stack_t stack, void* data);
|
||
```
|
||
这两个方法可以很方便的把数据添加到栈和从栈弹出,`push`方法`data`传入需要入队数据的地址,`pop`方法`data`传入需要接收出队数据的地址,这两个方法`data`都可以传入NULL,那只是一个占位。操作成功返回1,失败返回0。
|
||
```c
|
||
void test(void)
|
||
{
|
||
stack_t stack = stack(int, 10);
|
||
int i = 0;
|
||
|
||
for (i = 0; i < stack_capacity(stack); i++)
|
||
{
|
||
stack_push(stack, &i);
|
||
}
|
||
stack_pop(stack, NULL);
|
||
stack_pop(stack, NULL);
|
||
|
||
_stack(stack); // 成对使用,用完即删除
|
||
}
|
||
```
|
||
|
||
|
||
### stack的大小、容量和数据大小
|
||
```c
|
||
int stack_size(stack_t stack);
|
||
int stack_capacity(stack_t stack);
|
||
int stack_dsize(stack_t stack);
|
||
```
|
||
stack的`capacity`就是创建时候指定的容量,能存多少个栈元素,`size`是栈有多少个元素,`dsize`也就是创建时候传入的数据的大小,比如`int`,`dsize`就是`sizeof(int)`。
|
||
```c
|
||
void test(void)
|
||
{
|
||
stack_t stack = stack(int, 10);
|
||
int i = 0;
|
||
|
||
for (i = 0; i < stack_capacity(stack); i++)
|
||
{
|
||
stack_push(stack, &i);
|
||
}
|
||
stack_pop(stack, NULL);
|
||
stack_pop(stack, NULL);
|
||
printf("stack capacity=%d, size=%d, dsize=%d\r\n", stack_capacity(stack), stack_size(stack), stack_dsize(stack));
|
||
|
||
_stack(stack);
|
||
}
|
||
```
|
||
结果:
|
||
```
|
||
stack capacity=10, size=8, dsize=4
|
||
```
|
||
|
||
### stack数据的读写
|
||
```c
|
||
void* stack_data(stack_t stack, int index);
|
||
#define stack_at(stack, type, i)
|
||
```
|
||
`stack_data`方法就是根据索引来获取数据的地址,返回的则是指定的数据的地址,NULL则是失败。而`stack_at`则是在`stack_data`的基础上加多类型。
|
||
|
||
```c
|
||
void test(void)
|
||
{
|
||
stack_t stack = stack(int, 10);
|
||
int i = 0;
|
||
|
||
for (i = 0; i < stack_capacity(stack); i++)
|
||
{
|
||
stack_push(stack, &i);
|
||
}
|
||
stack_pop(stack, NULL);
|
||
stack_pop(stack, NULL);
|
||
for (i = 0; i < stack_size(stack); i++)
|
||
{
|
||
printf("stack[%d] = %d\r\n", i, stack_at(stack, int, i));
|
||
}
|
||
|
||
_stack(stack);
|
||
}
|
||
```
|
||
结果:
|
||
```
|
||
stack[0] = 0
|
||
stack[1] = 1
|
||
stack[2] = 2
|
||
stack[3] = 3
|
||
stack[4] = 4
|
||
stack[5] = 5
|
||
stack[6] = 6
|
||
stack[7] = 7
|
||
```
|
||
|
||
### stack数据存储索引
|
||
|
||
```c
|
||
#define stack_index(stack, index)
|
||
```
|
||
其实`stack_index`实际就是与`index`相对应,超出范围失败返回-1。
|
||
一般情况下,这个方法应用不多,也是在`stack_create`方法是传入了`base`,在`base`地址上来使用`stack_index`获取栈数据。
|
||
|
||
### stack空栈和满栈
|
||
```c
|
||
int stack_empty(stack_t stack);
|
||
int stack_full(stack_t stack);
|
||
```
|
||
这两个方法实际就是stack的`size`的大小关系,等于0为空,等于容量则满。
|
||
|
||
## 源码解析
|
||
|
||
### stack结构体
|
||
|
||
stack容器的所有结构体都是隐式的,也就是不能直接访问到结构体成员的,这样子的方式保证了模块的独立与安全,防止外部调用修改结构体的成员导致stack存储结构的破坏。所以stack解析器只留了唯一一个stack的声明在头文件,然后结构体的定义都在源文件。只能使用stack容器提供的方法对stack对象进行操作。
|
||
stack类型声明
|
||
```c
|
||
typedef struct STACK *stack_t;
|
||
```
|
||
使用时候,只是用`stack_t`即可。
|
||
|
||
```c
|
||
typedef struct STACK
|
||
{
|
||
void* base; /* base address of data */
|
||
int cst; /* base const */
|
||
int dsize; /* size of stack data */
|
||
int capacity; /* capacity of stack */
|
||
int top; /* index of stack top */
|
||
} STACK;
|
||
```
|
||
`STACK`结构体中包含了5个成员,`base`(栈结构数据缓冲区的基地址),`cst`(指示base的空间是否是create方法时候传进来),`dsize`(每个数据的大小),`capacity`(栈的容量),`top`(指示下一个栈数据索引,也就是`top`就表示`size`)。
|
||
|
||
stack容器最主要的问题就是解决数据先进后出的问题,其他创建删除是完成空间的初始化等基本初始化操作。
|
||
|
||
```c
|
||
#define at(i) (((unsigned char *)(stack->base))+(i)*(stack->dsize)) /* address of void array */
|
||
int stack_push(stack_t stack, void* data)
|
||
{
|
||
if (!stack) return 0;
|
||
if (stack_full(stack)) return 0; // 入栈之前先判断栈是否满了
|
||
if (data) memcpy(at(stack->top), data, stack->dsize); // 将数据复制到栈顶
|
||
stack->top++; // 栈顶增加
|
||
return 1;
|
||
}
|
||
int stack_pop(stack_t stack, void* data)
|
||
{
|
||
if (!stack) return 0;
|
||
if (stack_empty(stack)) return 0; // 出栈之前先判断栈是否为空
|
||
stack->top--; // 因为top指向下一个数据的索引,所以这里要先减1了
|
||
if (data) memcpy(data, at(stack->top), stack->dsize); // 再将数据复制出去
|
||
return 1;
|
||
}
|
||
```
|