鉴于您的数据结构表示堆栈上的一个节点,以及实际的堆栈本身:
typedef struct node {
void *dataptr;
struct node *link;
} NODE;
typedef struct {
int count;
NODE *top;
} STACK;
您将按如下方式初始化堆栈:
STACK myStack;
myStack.count = 0;
myStack.top = NULL;
这基本上给你一个空堆栈。我将使用top 来确定堆栈是否为空 - 您可以使用count 或top(分别为0 或NULL)来完成这项工作,但最好选择一个并坚持下去,以防您将来编写一些错误的代码,其中缓存计数和实际计数失步:-)
要将一个节点压入堆栈,这是一个简单的操作:
- 分配新节点并设置有效负载 (1)。
- 将新节点的链接指向当前头。
- 将头部指向该新节点 (3)。
- 增加计数 (4)。
下面的代码展示了如何做到这一点:
/* 1 */ NODE *newNode = malloc (sizeof (NODE)); // should check for failure.
newNode->dataptr = NULL;
/* 2 */ newNode->link = myStack.top;
/* 3 */ myStack.top = newNode;
/* 4 */ myStack.count++;
这将推送到空堆栈或填充堆栈。空堆栈的边缘情况将看到newNode.link 设置为NULL,然后myStack.top 设置为newNode,这是正确的行为。
从堆栈中弹出一个节点:
- 保存当前磁头 (1)。
- 如果当前 head 不为 NULL,则将 head 前进到其链接(并减少计数)。
- 返回保存的当前磁头 (3)。
翻译成代码的是:
/* 1 */ NODE *popNode = myStack.top;
/* 2 */ if (myStack.top != NULL) {
myStack.top = myStack.top->link;
myStack.count--;
}
/* 3 */ return popNode;
这将返回弹出节点的地址,如果堆栈为空,则返回 NULL。
整套操作可以封装如下。希望 cmets 能够结合上面的 cmets 使其不言自明,但请随时提出任何疑虑,我将通过编辑解决它们。
// Error codes (probably should be enums).
#define STACK_ERR_OKAY 0
#define STACK_ERR_NOTEMPTY 1
#define STACK_ERR_NOPAYLOAD 2
#define STACK_ERR_NOMEMORY 3
// Structures.
typedef struct sNode {
void *data; // Payload pointer.
struct sNode *next; // Link to next node.
} tNode;
typedef struct {
int count; // Count for fast sizing.
NODE *top; // First node.
} tStack;
// Make a new stack returning its pointer or NULL on failure.
tStack *stackNew (void) {
tStack stack = malloc (sizeof (tStack));
if (stack != NULL) {
stack->count = 0;
stack->top = NULL;
}
return stack;
}
// Delete a current stack, must be empty first.
int stackDel (tStack *stack) {
if (stack->top != NULL)
return STACK_ERR_NOT_EMPTY;
free (stack);
return STACK_ERR_OK;
}
// Push a pointer payload (no NULLs allowed) onto the stack.
int stackPush (tStack *stack, void *payload) {
if (payload == NULL)
return STACK_ERR_NOPAYLOAD;
tNode *node = malloc (sizeof (tNode));
if (node == NULL)
return STACK_ERR_NOMEMORY;
node->data = payload;
node->next = stack->top;
stack->top = node;
stack->count++;
return STACK_ERR_OK;
}
// Pop a pointer payload from the stack. Returns NULL if stack was empty.
int stackPop (tStack *stack) {
tNode *node = stack->top;
if (node == NULL) {
return NULL;
stack->top = node->next;
stack->count--;
return node->data;
}
// Get stack size.
int stackSize (tStack *stack) {
return stack->count;
}
我坚持在删除之前堆栈必须为空的原因是因为代码不确定有效负载是什么。它可能是一个指向内存块(浅)的简单指针,在这种情况下您可以使用:
void stackDel (tStack *stack) {
tNode *node = stackPop (stack);
while (node != NULL) {
free (node);
node = stackPop (stack);
}
free (stack);
}
但是,如果它是一个指向内存的指针,它持有其他指向内存的指针,那么自动释放它所引用的内存就会有更大的问题(它可能最好作为对 API 调用者的回调来完成,因为它将是唯一的野兽知道如何正确释放内存)。将其作为预先调用者的先决条件要简单得多。