Objectives of this lecture
Why do we need another implementation method?
Using malloc
to obtain memory at run-time.
e.g. int
*int_ptr;
int_ptr=(int *) malloc(2);
*int_ptr =17;
int *int_ptr;
int_ptr=(int *) malloc(sizeof(int));
*int_ptr =17;
Stack Implementation using dynamic memory allocation
User Interface:
This can include the following:
typedef
char ITEM_TYPE;
typedef
struct node_type { ITEM_TYPE item;
struct node_type *next;
} NODE_TYPE;
typedef
NODE_TYPE *NODE_PTR;
typedef
NODE_TYPE *STACK_TYPE;
typedef
enum{FALSE,TRUE} BOOLEAN;
void
create_stack(STACK_TYPE *stack);
void
destroy_stack(STACK_TYPE *stack);
BOOLEAN
empty_stack(STACK_TYPE *stack);
BOOLEAN
full_stack(STACK_TYPE *stack);
void
push(STACK_TYPE *stack, ITEM_TYPE newitem);
void
pop(STACK_TYPE *stack, ITEM_TYPE *old_item);
Notice that:
1.
The user
can change the type of ITEM_TYPE
2.
The
pointer field next in the declaration of struct node_type is declared to be of type struct node_type.
i.e. struct node_type is used before its declaration is completed. This
is called self-referencing definition and is allowed in C in this case.
3.
NODE_PTR and STACK_TYPE are
pointer types and they are actually the same.
The two are being used to distinguish between the special pointer (stack)
that points to the top node and other pointers that points to other nodes.
4.
The
function prototypes are the same as those in array implementation. Thus, even though stack is a pointer, we are
still passing its address to ensure compatibility. This (passing address of a pointer variable) is called double
indirection.
Implementation details:
#include
<stdlib.h> /*needed for malloc */
#include
"stack.h"
/*
initializes stack to NULL */
void
create_stack(STACK_TYPE *stack)
{
*stack=NULL;
}
/*
return the memory cells occupied by elements of stack to the system */
void
destroy_stack(STACK_TYPE *stack)
{ NODE_PTR temp_ptr;
while (*stack != NULL)
{ temp_ptr = *stack;
*stack=temp_ptr->next;
free(temp_ptr);
}
}
/*
returns true if stack is NULL) */
BOOLEAN
empty_stack(STACK_TYPE *stack)
{
if (*stack== NULL)
return TRUE;
else
return FALSE;
}
/*always
return false since in this case, stack is never full */
BOOLEAN
full_stack(STACK_TYPE *stack)
{ return FALSE;
}
/*create
a node, add newitem to it and link it to top of stack */
void
push(STACK_TYPE *stack, ITEM_TYPE newitem)
{ NODE_PTR temp_ptr;
temp_ptr=(NODE_PTR)
malloc(sizeof(NODE_TYPE));
if (temp_ptr != NULL)
{
temp_ptr->item=newitem;
temp_ptr->next=*stack;
*stack=temp_ptr;
}
}
/*
returns the item at top of stack and returns its storage to the system */
void
pop(STACK_TYPE *stack, ITEM_TYPE *old_item)
{ NODE_PTR temp_ptr;
temp_ptr=*stack;
*old_item=temp_ptr->item;
*stack=temp_ptr->next;
free(temp_ptr);
}
Notice that:
1.
The destroy_stack is different from that of array implementation. The storage occupied my elements of the
stack is physically returned to the system.
2.
To push
an element, memory has to be physically created for it and when an element is popped,
its memory is returned to the system.
Thus, no memory is wasted and the stack is never full –really?
3.
Memory
on the heap is not infinite. If malloc
cannot create a storage, it returns null. Thus, the push function has a
potential bug.
4.
The full_stack is not necessary in this implementation. It is only being used for compatibility
purpose. The compiler may warn that the
pointer variable stack in this function is never used. This should be ignored.