C泛型栈的实现

首先看一个基于int类型的栈的实现,定义一个栈的结构体,定义如下:

typedef struct {
int *elems;               // 指向栈空间地址指针
int total_elem;            // 当前栈空间中的元素个数
int alloc_length;           // 栈空间的实际大小
}Stack;

对栈的操作,主要要实现栈空间的初始化,释放以及入栈和出栈,需要实现以下四个基本函数:

void StackNew(Stack *s);               // 创建一个栈空间
void StackDispose(Stack *s);            // 释放栈空间
void StackPush(Stack *s, int elem);       // 将元素elem入栈
int StackPop(Stack *s);                // 出栈,并返回出栈元素

对于栈空间的初始化,其实非常简单,包含三个操作:

1、  设置栈空间元素个数,此时相当于初始化一个空栈

2、  设置默认的栈空间大小

3、  申请栈空间

以下是StackNew的源码实现

s->total_elem = 0;
s->alloc_length = 4;     // 默认为4
s->elems = malloc(4 * sizeof(int));          // 申请栈空间
assert(s->elems != NULL);   

对于栈空间的释放就更简单了,两句话就搞定了。

free(s->elems);
s->elems = NULL;

接下来就是栈操作的最重要的两个操作—入栈和出栈

对于入栈可概括为如下:

1、  是否有空余的空间

2、  如果有则执行4

3、  如果没有则重新分配栈空间,并拷贝原栈中元素到新栈空间中

4、  入栈,元素个数自增1

实现代码:
if (s->total_elem == s->alloc_length) {
s->alloc_length *= 2;    // 此处采用栈空间倍增的策略,你也可以换为你认为好的
s->elems = realloc(s->elems, s->alloc_length * sizeof(int));
assert(s->elems != NULL);
}
s->elems[s->total_elem++] = elem;

可能你对realloc很疑惑,该函数先判断当前的指针是否有足够的连续空间,如果有,扩大s->elems指向的地址,并且将s->elems返回,如果空间不够,先按照指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来s->elems所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

在来看看最后的出栈操作

assert(s->total_elem > 0);
s->total_elem--;
return s->elems[s->total_elem];
也是相当的简单吧!

这就实现了一个简单的栈,当然这个只是针对int数据类型而言的。

那么如何设计一个更通用的呢?其实也不是很复杂,往下看就知道了

首先我们对上面定义的结构体进行稍微的修改,增加一个元素大小的成员,并将int *类型指针更改为void *类型:

typedef struct {
void *elems;
int elem_size;
int total_elem;
int alloc_length;
}Stack;

同样我们需要对初始化函数稍作修改,增加一个元素大小参数以及出栈时增加一个传出参数获取栈顶元素,并将入栈操作的第二个参数改为指针。其他的都基本上与上面类似,就直接贴源码了。

void StackNew(Stack *s, int elemSize) {
	assert(elemSize > 0);
	s->elem_size = elemSize;
	s-> total_elem  = 0;
	s->alloc_length = 4;
	s->elems = malloc(4 * elemSize);
	assert(s->elems);
}

void StackDispose(Stack *s) {
	free(s->elems);
	s->elems = NULL;
}

与上面的栈实现的主要区别在于需要手动计算元素地址,因为void*类型,系统比不知道该怎么处理。所以需要自己计算出入栈和出栈时元素的地址。

static void StackGrow(Stack *s) {
	s->alloc_length *= 2;
	s->elems = realloc(s->elems, s->alloc_length * s->elem_size);
	assert(s->elems != NULL);
}

void StackPush(Stack *s, void *elemAddr) {
	if (s-> total_elem  == s->alloc_length) {
		StackGrow(s);
	}
	void *target  = (char *)s->elems + s-> total_elem  * s->elem_size;
	memcpy(target, elemAddr, s->elem_size);
	s->logic_length++;
}

void StackPop(Stack *s, void *elemAddr) {
	assert(s->logic_length > 0);
	s-> total_elem --;
	void *source = (char *)s->elems + s-> total_elem  * s->elem_size;
	memcpy(elemAddr, source, s->elem_size);
}
通过
(char *)s->elems + s-> total_elem  * s->elem_size;

语句,可计算出入栈或出栈时的栈地址,从而实现入栈或出栈。












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值