There are some problems in which we want to store information, and we want to access the more recently stored information first. A data structure that allows us to access information in this manner is called a stack. It is so-named because it resembles a stack of dishes. When you want a dish, you take it off the stack of dishes. When you clean a dish, you put it back on top of the stack of dishes. The same holds with a stack of data. When you store a new data item, you store it on top of the stack. When you want to retrieve a data item, you retrieve the top item from the stack. Note that this data item will be the one most recently stored on the stack.
A couple examples where you want to access information in this manner in the real world are as follows:
B A mainBy convention stacks are shown growing upwards. While a function is executing, its stack record will be on top of the stack, and hence its information will be readily available. In the above case, B is currently executing and so we can readily access its parameters and local variables. If B calls a new function C, then a stack record for C is created and pushed onto the call stack. C now has access to its local variables and parameters. When C returns, we want B to resume executing, and thus we want access to B's stack record. By simply popping C's stack record off the call stack, we again have access to B's stack record. Hence a stack is the perfect data structure for implementing a call stack, because we always want access to the most recently called function, and when it returns, we want access to the function that called it.
Our interface for a stack will be as follows:
Notice that in keeping with our policy of information hiding, we return a "void *" handle to the user. We will declare the structs that implement our stack within the .c file that implements the stack.
There is an argument that could be made for allowing the user to specify either a default initial size for the stack, or alternatively, a maximum size of the stack. I have chosen not to do so for simplicity sake. A full strength commercial version of a stack would probably give the user both options.
Stacks can be implemented using either arrays or lists. With an array we add items to the end of the array and with a list we add items to the end of the list. An array uses less space, because it does not require a next pointer, and it tends to be more efficient because the stack items are kept physically continguous and because the bookkeeping is slightly simpler. However, arrays are fixed size and hence the user must either specify a maximum bound on the size of the array or we must be prepared to re-size the array if the array overflows. Lists are more flexible because they can grow arbitrarily large without having to worry about overflow. Of course if they get too large, malloc could run out of memory, but lists typically do not become that large.
The array implementation needs to maintain three pieces of information:
Here is the struct used to implement the array. Note that is a container object for the array. The "separate" element that we use to hold the stack is an array:
#define INIT_SIZE 100 #define RESIZE_FACTOR 2 typedef struct { void **values; // The array of values. int top_of_stack; // The top of the stack. int size; // Current capacity of the stack. } Stack;Note that each entry in the array is a "void *" pointer, and hence our values array is a "void **". The INIT_SIZE is the initial size of our stack and the RESIZE_FACTOR is the amount by which we increase the stack capacity if the array becomes full. In this case we double the array's size each time it becomes full. If the user were allowed to specify a default initial size for the stack, or a maximum bound for the stack, then we would omit the INIT_SIZE constant and instead malloc an array of the size specified by the user.
The code for the array implementation of the stack can be found here.
The book uses a typedef to equate a stack with a list. I do not like this implementation because it requires that a stack be a list. I prefer instead to create a container object for a stack that contains a pointer to either a singly or doubly-linked list:
typedef struct { Dllist *list; } Stack;Push will prepend items to the front of the list and pop will remove the first item from the list. Hence both pop and top can use dll_first to find the top element of the list. The code for the list implementation can be found here.