如果我们分成两个结构,它会更通用,可能更容易理解/使用:一个用于堆栈控制,另一个用于堆栈中的元素:
// stack element
struct _stackelem;
typedef struct _stackelem elem_t;
struct _stackelem {
elem_t *prev;
elem_t *next;
char elem;
int precedence;
};
// stack control
struct _stack;
typedef struct _stack stack_t;
struct _stack {
elem_t *head;
elem_t *tail;
long maxcnt;
long curcnt;
};
stack_t opStack;
other_func()
{
opStack.tail = malloc(sizeof(elem_t));
}
请注意,我已将“head”添加到堆栈控件,并将“prev”添加到 elem_t。这允许 stack_t 是一个双向链表,并且可以在需要时实现除了堆栈之外的队列等。另外,我在 elem_t 中添加了一个“优先级”——更多内容如下。
这是您的原始函数 [修正类似于 A.S.H 的]:
// RETURNS: -1=empty stack
char
poptheop_original(stack_t *stk)
{
elem_t *tmp;
char retval;
tmp = stk->tail;
// stack is empty
if (tmp == NULL)
retval = -1;
// pop last element
else {
retval = tmp->elem;
stk->tail = tmp->next;
free(tmp);
}
return retval;
}
现在,“(*s1)”已替换为“stk->tail”。我们避免了传递“**”,代码执行速度也一样快。
但是,如果您只需要“elem”,我会考虑将您的操作堆栈重新实现为一个可重新分配的数组(例如“char *opstack”)。
使用链表方式更适合返回更复杂的东西。这就是我添加“优先级”的原因。这是您的原始函数经过调整,因此您可以同时使用“elem”和“precedence”:
// RETURNS: elem is -1 on empty stack
elem_t
poptheop_struct(stack_t *stk)
{
elem_t *tmp;
elem_t retval;
tmp = stk->tail;
// stack is empty
if (tmp == NULL) {
retval.elem = -1;
retval.precedence = -1;
}
// pop last element
else {
retval = *tmp;
stk->tail = tmp->next;
free(tmp);
}
return retval;
}
请注意,这个函数在内部仍然是“免费的”。但是,它通过 value 将结构传回。几乎从来都不是一件好事。
这是最终版本,它返回指向弹出的 elem_t 的指针:
// RETURNS: NULL is empty
elem_t *
poptheop_pointer(stack_t *stk)
{
elem_t *retval;
retval = stk->tail;
// pop stack
if (retval != NULL)
stk->tail = retval->next;
return retval;
}
你现在必须自己释放指针——这是缺点。但好处是 elem_t 可以有许多数据元素,而不仅仅是 elem 和优先级,并且可以快速执行而无需进行不必要的额外数据复制。
拆分 stack_t 的另一个好处是您可以隐藏堆栈如何实现的细节。它可以是一个单链表 [正如你所做的那样]、一个双链表或我之前提到的 realloc 数组。
如果您愿意,作为练习,将 stack_t 重新实现为可重新分配的数组。这就是我添加“maxcnt”和“curcnt”的原因。如果结果证明数组方法更合适,则可以从 elem_t 中完全消除“prev”和“next”。
作为进一步的提示:您只需要在推送期间重新分配并且仅当 curcnt 即将溢出 maxcnt 时。将 realloc 设置为 maxcnt + slop_factor 以防止执行过多的 realloc。这将使它“聪明”。
这可能是最适合你的实现——YMMV