Pages

Friday, July 3, 2015

Implementing a generic C Stack


In an earlier post I included the header file and why all kinds of variables should be there in the struct of our stack. This post is about the implementation of the functions and their prototypes.
First lets look at the implementation and then we will see why this and that happens in a particular way.
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

void newStack(Stack* stack,int size,int elementSize,void
(*clearmem)(void* )){
   
stack->size=size;
    stack->currentIndex=0;
   
stack->items=malloc(size*elementSize);
   
stack->elementSize=elementSize;
   
stack->clearmem=clearmem;
}
void push(Stack* stack,void* item){
   
if(stack->currentIndex>=stack->size){
       
printf("not enough space");
        return;
    }else{
        void*
addr=(char*)stack->items+(stack->currentIndex*stack->elementSize);
       
memcpy(item,addr,stack->elementSize);
       
stack->currentIndex++;
    }
}
void pop(Stack* stack,void* destination){
   
if(stack->currentIndex<=0){
       
printf("sorry empty stack");
        return;
    }else{
       
stack->currentIndex--;
       
memcpy((char*)stack->items+(stack->currentIndex*stack->elementSize),destination,stack->elementSize);
    }
}
void dispose(Stack* stack){
    if(stack->clearmem!=NULL){
       
while(stack->currentIndex>=0){
           
stack->currentIndex--;
           
stack->clearmem((char*)stack->items+(stack->currentIndex*stack->elementSize));
        }
    }
   
free(stack->items);
}

Now the newStack function dosent do anything interesting instead it just initializes the stuff in our struct. The push function has some interesting stuff. The idea of the function is we copy everything from the starting address given with void* to the stack covering the length of a single item (we got it when we initialized our stack). Interesting thing here is how to compute the address within our stack memory block.
Void dosent associate size information. That is if you have int* I and you say i+1 you would get the address of the byte that is 4 bytes a way from the starting address. This happens because we know that int is 4 bytes long (normally). But since we are dealing with void first we cast it in to a char* so that it would be assumed as one byte long and pointer arithmetic would be like normal calculation. That is if we have char* c and we do c+1 it would give the address of the byte that is 1 byte away from the starting address.
Now to the pop function. Why that is needed and address to place the item in the stack instead of just returning the address within our stack. Well think of what happens when one push another item after the pop we would overwrite it. That’s not what we want right?. Other thing is the pointer arithmetic which is mentioned above.
Finally, the dispose function destroys the stack and its content. We are not responsible for freeing up memory for items that was poped instead we would do the cleanup for items that are still in the stack by using the memclear function given to us by our client at the initialization. After cleaning items we would free the memory that was allocated for our stack using free function.

Now its done