Explanation of how stacks work in C
I am just wanting a simple explanation of the linking process when pushing data onto a stack. I know how to build on using the code from my book, but I am not really sure I understand how the process works when you move the stack head link from one to the next开发者_Python百科.
For stacks like:
typedef struct node
{
void dataptr;
struct node* link;
}STRUCT_NODE;
typedef struct
{
int count;
STACK_NODE* top;
}STACK;
How do you change the link to point to the new data pushed on the stack. Also I do not know
Stacks can be implemented in various ways, but given the way you phrase your question I'm assuming your stack is just a linked list, something like
head
↓
A → B → C → D → 0
"when you move the stack head link from one to the next" the picture just changes to:
head
↓
A → B → C → D → 0
Of course A is not reachable any more in this graph, so you'd better have another pointer to it somewhere (be it only to dispose of it), but this is the gist how how the stack is popped (by making head = head->next
if each node in the stack is a struct node
with a next
field that's a struct node*
, and of course head
is a struct node*
as well).
That's for popping something off the stack (and you should free the memory used by A
in that case). In detailed steps, it would be:
1/ Saving the old head.
head
↓
old → A → B → C → D → 0
2/ Adjusting the head.
head
↓
old → A → B → C → D → 0
3/ Returning the old head (pointed at by old
).
If instead you're talking about pushing something onto the stack, that's an operation that involves:
1/ Creating a new element.
head
↓
Z A → B → C → D → 0
2/ Pointing it towards the current head
head
↓
Z → A → B → C → D → 0
3/ Adjusting the head to point to it.
head
↓
Z → A → B → C → D → 0
Given your data structures representing a node on the stack, and the actual stack itself:
typedef struct node {
void *dataptr;
struct node *link;
} NODE;
typedef struct {
int count;
NODE *top;
} STACK;
you would initialise a stack as follows:
STACK myStack;
myStack.count = 0;
myStack.top = NULL;
This basically gives you an empty stack. I'm going to use top
to decide if the stack is empty - you could use either count
or top
(as 0
or NULL
respectively) to do that job but it's a good idea to choose one and stick with it in case you ever write some buggy code in future where the cached count and actual count get out of step :-)
To push a node onto the stack, it's a simple operation of:
- allocate the new node and set the payload (1).
- point the new node's link to the current head.
- point the head to that new node (3).
- increment the count (4).
The following code shows how you can do it:
/* 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++;
This will push onto either an empty stack or a populated one. The edge case of an empty stack will see newNode.link
set to NULL
, then myStack.top
set to newNode
, which is the correct behaviour.
To pop a node off the stack:
- save the current head (1).
- if the current head isn't NULL, advance head to its link (and decrement count).
- return the saved current head (3).
which , translated to code, is:
/* 1 */ NODE *popNode = myStack.top;
/* 2 */ if (myStack.top != NULL) {
myStack.top = myStack.top->link;
myStack.count--;
}
/* 3 */ return popNode;
This returns either the address of the popped node, or NULL
if the stack was empty.
The whole set of operations could be encapsulated as follows. Hopefully the comments will make it self-explanatory in conjunction with the comments above but feel free to raise any concerns and I'll address them with an edit.
// 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;
}
The reason I've insisted that a stack must be empty before deleting is because the code doesn't know for certain what the payload is. It could be a simple pointer to a block of memory (shallow) in which case you could just use:
void stackDel (tStack *stack) {
tNode *node = stackPop (stack);
while (node != NULL) {
free (node);
node = stackPop (stack);
}
free (stack);
}
but, if it's a pointer to memory holding other pointers to memory, it's more problematic to automatically free the memory it references (it would probably be best done as a callback to the caller of the API since it would be the only beast that knew for sure how to properly free the memory). Far simpler to make that a pre-requisite on the caller up front.
Say you have some STACK called my_stack, and a STACK_NODE called my_node. To add my_node to my_stack, do this:
my_node.link = my_stack.top;
my_stack.top = &my_node;
my_stack.count = my_stack.count + 1;
精彩评论